public/functions/Set-PowerStubCommandVisibility.ps1

<#
.SYNOPSIS
    Changes the visibility/lifecycle stage of a PowerStub command.
 
.DESCRIPTION
    Renames a command script file to change its visibility prefix:
    - Alpha: alpha.commandname.ps1 (work-in-progress, requires Enable-PowerStubAlphaCommands)
    - Beta: beta.commandname.ps1 (testing, requires Enable-PowerStubBetaCommands)
    - Production: commandname.ps1 (always visible)
 
    If the target file already exists, prompts for confirmation unless -Force is specified.
 
.PARAMETER Stub
    The name of the stub containing the command.
 
.PARAMETER Command
    The name of the command (without visibility prefix).
 
.PARAMETER Visibility
    The target visibility level: Alpha, Beta, or Production.
 
.PARAMETER Force
    Skip confirmation prompt if target file already exists (replaces it).
 
.EXAMPLE
    Set-PowerStubCommandVisibility -Stub DevOps -Command deploy -Visibility Beta
 
    Promotes the deploy command to beta stage.
 
.EXAMPLE
    Set-PowerStubCommandVisibility -Stub DevOps -Command deploy -Visibility Production -Force
 
    Promotes to production, replacing any existing production version.
 
.OUTPUTS
    PSCustomObject with OldPath, NewPath, and Visibility properties.
#>


function Set-PowerStubCommandVisibility {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Stub,

        [Parameter(Mandatory = $true, Position = 1)]
        [string]$Command,

        [Parameter(Mandatory = $true, Position = 2)]
        [ValidateSet('Alpha', 'Beta', 'Production')]
        [string]$Visibility,

        [switch]$Force
    )

    # Verify stub exists
    $stubs = Get-PowerStubConfigurationKey 'Stubs'
    if (-not ($stubs.Keys -contains $Stub)) {
        throw "Stub '$Stub' not found. Use Get-PowerStubs to see registered stubs."
    }

    $stubRoot = $stubs[$Stub]
    $commandsPath = Join-Path $stubRoot 'Commands'

    if (-not (Test-Path $commandsPath)) {
        throw "Commands folder not found for stub '$Stub'."
    }

    # Find all versions of this command (alpha, beta, production)
    $versions = @{
        Alpha      = $null
        Beta       = $null
        Production = $null
    }

    # Check for direct files
    $alphaFile = Join-Path $commandsPath "alpha.$Command.ps1"
    $betaFile = Join-Path $commandsPath "beta.$Command.ps1"
    $prodFile = Join-Path $commandsPath "$Command.ps1"

    # Check for subfolder files
    $subfolderPath = Join-Path $commandsPath $Command
    $alphaSubFile = Join-Path $subfolderPath "alpha.$Command.ps1"
    $betaSubFile = Join-Path $subfolderPath "beta.$Command.ps1"
    $prodSubFile = Join-Path $subfolderPath "$Command.ps1"

    # Determine which files exist and where
    $isSubfolder = $false

    if (Test-Path $alphaFile) { $versions.Alpha = $alphaFile }
    elseif (Test-Path $alphaSubFile) { $versions.Alpha = $alphaSubFile; $isSubfolder = $true }

    if (Test-Path $betaFile) { $versions.Beta = $betaFile }
    elseif (Test-Path $betaSubFile) { $versions.Beta = $betaSubFile; $isSubfolder = $true }

    if (Test-Path $prodFile) { $versions.Production = $prodFile }
    elseif (Test-Path $prodSubFile) { $versions.Production = $prodSubFile; $isSubfolder = $true }

    # Count how many versions exist
    $existingVersions = $versions.GetEnumerator() | Where-Object { $_.Value -ne $null }
    $existingCount = @($existingVersions).Count

    if ($existingCount -eq 0) {
        throw "Command '$Command' not found in stub '$Stub'."
    }

    # Determine source file (the one to rename)
    $sourceFile = $null
    $sourceVisibility = $null

    if ($existingCount -eq 1) {
        # Only one version exists, use it
        $sourceVisibility = $existingVersions[0].Key
        $sourceFile = $existingVersions[0].Value
    }
    else {
        # Multiple versions exist - use precedence order based on enabled modes
        $alpha = Get-PowerStubConfigurationKey 'EnablePrefix:Alpha'
        $beta = Get-PowerStubConfigurationKey 'EnablePrefix:Beta'

        if ($alpha -and $versions.Alpha) {
            $sourceVisibility = 'Alpha'
            $sourceFile = $versions.Alpha
        }
        elseif ($beta -and $versions.Beta) {
            $sourceVisibility = 'Beta'
            $sourceFile = $versions.Beta
        }
        elseif ($versions.Production) {
            $sourceVisibility = 'Production'
            $sourceFile = $versions.Production
        }
        else {
            # Fall back to first available
            $sourceVisibility = $existingVersions[0].Key
            $sourceFile = $existingVersions[0].Value
        }

        Write-Verbose "Multiple versions exist. Using active version: $sourceVisibility ($sourceFile)"
    }

    # Check if already at target visibility
    if ($sourceVisibility -eq $Visibility) {
        Write-Host "Command '$Command' is already at $Visibility visibility." -ForegroundColor Yellow
        return [PSCustomObject]@{
            Command    = $Command
            Stub       = $Stub
            Path       = $sourceFile
            Visibility = $Visibility
            Changed    = $false
        }
    }

    # Determine target file path
    $sourceDir = Split-Path $sourceFile -Parent
    $extension = [System.IO.Path]::GetExtension($sourceFile)

    $targetFileName = switch ($Visibility) {
        'Alpha' { "alpha.$Command$extension" }
        'Beta' { "beta.$Command$extension" }
        'Production' { "$Command$extension" }
    }
    $targetFile = Join-Path $sourceDir $targetFileName

    # Check if target already exists (and it's not the source)
    if ((Test-Path $targetFile) -and ($targetFile -ne $sourceFile)) {
        if (-not $Force) {
            $response = Read-Host "Target file '$targetFileName' already exists. Replace it? (y/N)"
            if ($response -notmatch '^[Yy]') {
                Write-Host "Operation cancelled." -ForegroundColor Yellow
                return
            }
        }
        # Remove existing target
        Remove-Item $targetFile -Force
        Write-Verbose "Removed existing target file: $targetFile"
    }

    # Rename the file
    if ($PSCmdlet.ShouldProcess($sourceFile, "Rename to $targetFileName")) {
        Rename-Item -Path $sourceFile -NewName $targetFileName -Force

        Write-Host "Changed '$Command' visibility: $sourceVisibility -> $Visibility" -ForegroundColor Green

        return [PSCustomObject]@{
            Command       = $Command
            Stub          = $Stub
            OldPath       = $sourceFile
            NewPath       = $targetFile
            OldVisibility = $sourceVisibility
            NewVisibility = $Visibility
            Changed       = $true
        }
    }
}