scripts/modules/shared/validation-functions.ps1

# strangeloop Setup - Shared Validation Functions
# Version: 1.0.0


# Parameter validation and compatibility checking functions

function Test-ParameterValidation {
    <#
    .SYNOPSIS
        Validates parameters and checks for deprecated usage patterns
    
    .PARAMETER BoundParameters
        The $PSBoundParameters from the calling function
    
    .PARAMETER AllowedParameters
        Array of parameter names that are allowed
    
    .RETURNS
        Hashtable with validation results
    #>

    param(
        [Parameter(Mandatory)]
        [hashtable]$BoundParameters,
        
        [string[]]$AllowedParameters = @()
    )
    
    $result = @{
        IsValid = $true
        Warnings = @()
        Errors = @()
        DeprecatedParameters = @()
    }
    
    # Check for deprecated parameters
    $deprecatedParams = @{
        'mode' = 'The -mode parameter is deprecated. Use targeted phase execution instead.'
        'modular' = 'The -modular parameter is deprecated. All setups now use the unified approach.'
        'monolith' = 'The -monolith parameter is deprecated. All setups now use the unified approach.'
        'approach' = 'The -approach parameter is deprecated. All setups now use the unified approach.'
    }
    
    foreach ($paramName in $BoundParameters.Keys) {
        if ($deprecatedParams.ContainsKey($paramName.ToLower())) {
            $result.DeprecatedParameters += $paramName
            $result.Warnings += $deprecatedParams[$paramName.ToLower()]
        }
    }
    
    # Check for conflicting parameters
    $conflictingParams = @(
        @('phases', 'from-phase'),
        @('phases', 'to-phase'),
        @('list-phases', 'phases'),
        @('list-phases', 'from-phase'),
        @('list-phases', 'to-phase'),
        @('help', 'phases'),
        @('help', 'from-phase'),
        @('help', 'to-phase')
    )
    
    foreach ($conflict in $conflictingParams) {
        if ($BoundParameters.ContainsKey($conflict[0]) -and $BoundParameters.ContainsKey($conflict[1])) {
            $result.Errors += "Cannot use -$($conflict[0]) and -$($conflict[1]) together"
            $result.IsValid = $false
        }
    }
    
    # Validate allowed parameters if specified
    if ($AllowedParameters.Count -gt 0) {
        foreach ($paramName in $BoundParameters.Keys) {
            if ($paramName -notin $AllowedParameters) {
                $result.Warnings += "Parameter -$paramName is not recognized"
            }
        }
    }
    
    return $result
}

function Test-PathValidation {
    <#
    .SYNOPSIS
        Validates project path format and accessibility
    
    .PARAMETER ProjectPath
        The project path to validate
    
    .PARAMETER CreateIfMissing
        Whether to create the path if it doesn't exist
    
    .RETURNS
        Hashtable with validation results
    #>

    param(
        [Parameter(Mandatory)]
        [string]$ProjectPath,
        
        [switch]$CreateIfMissing
    )
    
    $result = @{
        IsValid = $true
        Errors = @()
        Warnings = @()
        NormalizedPath = $ProjectPath
        PathType = 'Unknown'
    }
    
    # Normalize path separators
    $normalizedPath = $ProjectPath.Replace('/', '\')
    $result.NormalizedPath = $normalizedPath
    
    # Determine path type
    if ($normalizedPath -match '^[A-Za-z]:\\') {
        $result.PathType = 'Windows'
    } elseif ($ProjectPath.StartsWith('/') -or $ProjectPath.Contains('/home/')) {
        $result.PathType = 'WSL'
        $result.NormalizedPath = $ProjectPath  # Keep original format for WSL paths
    } else {
        $result.PathType = 'Relative'
        $result.Warnings += "Relative paths may cause issues. Consider using absolute paths."
    }
    
    # Check for invalid characters
    $invalidChars = [IO.Path]::GetInvalidPathChars()
    foreach ($char in $invalidChars) {
        if ($normalizedPath.Contains($char)) {
            $result.Errors += "Path contains invalid character: '$char'"
            $result.IsValid = $false
        }
    }
    
    # Check path length (Windows limitation)
    if ($result.PathType -eq 'Windows' -and $normalizedPath.Length -gt 260) {
        $result.Warnings += "Path length ($($normalizedPath.Length)) exceeds Windows limitation (260 characters)"
    }
    
    # Check if path exists or can be created
    if ($result.PathType -eq 'Windows') {
        try {
            if (-not (Test-Path $normalizedPath)) {
                if ($CreateIfMissing) {
                    $parentPath = Split-Path $normalizedPath -Parent
                    if (-not (Test-Path $parentPath)) {
                        $result.Errors += "Parent directory does not exist: $parentPath"
                        $result.IsValid = $false
                    }
                } else {
                    $result.Warnings += "Path does not exist: $normalizedPath"
                }
            }
        } catch {
            $result.Errors += "Cannot access path: $($_.Exception.Message)"
            $result.IsValid = $false
        }
    }
    
    return $result
}

function Test-LoopNameValidation {
    <#
    .SYNOPSIS
        Validates loop name format and availability
    
    .PARAMETER LoopName
        The loop name to validate
    
    .RETURNS
        Hashtable with validation results
    #>

    param(
        [Parameter(Mandatory)]
        [string]$LoopName
    )
    
    $result = @{
        IsValid = $true
        Errors = @()
        Warnings = @()
        SuggestedNames = @()
    }
    
    # Import loop functions to access loop registry
    $loopFunctionsPath = Join-Path $PSScriptRoot "loop-functions.ps1"
    if (Test-Path $loopFunctionsPath) {
        . $loopFunctionsPath
        
        # Check if loop exists
        if (-not (Test-LoopExists -LoopName $LoopName)) {
            $result.IsValid = $false
            $result.Errors += "Loop '$LoopName' is not in the known loops registry"
            
            # Suggest similar names
            $knownLoops = Get-KnownLoops
            $suggestions = $knownLoops.Keys | Where-Object { $_ -like "*$LoopName*" -or $LoopName -like "*$_*" }
            $result.SuggestedNames = $suggestions
            
            if ($suggestions.Count -gt 0) {
                $result.Warnings += "Did you mean one of these: $($suggestions -join ', ')?"
            }
        }
    } else {
        $result.Warnings += "Cannot validate loop name - loop-functions.ps1 not found"
    }
    
    # Validate name format
    if ($LoopName -match '[^a-z0-9\-]') {
        $result.Warnings += "Loop name contains unexpected characters. Expected format: lowercase letters, numbers, and hyphens only"
    }
    
    if ($LoopName.Length -lt 3) {
        $result.Warnings += "Loop name is very short. Consider using a more descriptive name"
    }
    
    return $result
}

function Test-PhaseParameterValidation {
    <#
    .SYNOPSIS
        Validates phase-related parameters for consistency
    
    .PARAMETER Phases
        Array of phase names to target
    
    .PARAMETER FromPhase
        Starting phase name
    
    .PARAMETER ToPhase
        Ending phase name
    
    .RETURNS
        Hashtable with validation results
    #>

    param(
        [string[]]$Phases = @(),
        
        [string]$FromPhase = '',
        
        [string]$ToPhase = ''
    )
    
    $result = @{
        IsValid = $true
        Errors = @()
        Warnings = @()
    }
    
    # Import phase functions to access phase information
    $phaseFunctionsPath = Join-Path $PSScriptRoot "phase-functions.ps1"
    if (Test-Path $phaseFunctionsPath) {
        . $phaseFunctionsPath
        
        $validPhases = Get-PhaseOrder
        
        # Validate individual phase names
        foreach ($phase in $Phases) {
            if (-not (Test-PhaseExists -PhaseName $phase)) {
                $result.IsValid = $false
                $result.Errors += "Unknown phase: $phase"
                
                # Suggest similar phase names
                $suggestions = $validPhases | Where-Object { $_ -like "*$phase*" -or $phase -like "*$_*" }
                if ($suggestions.Count -gt 0) {
                    $result.Warnings += "Did you mean: $($suggestions -join ', ')?"
                }
            }
        }
        
        # Validate range parameters
        if (-not [string]::IsNullOrEmpty($FromPhase) -and -not (Test-PhaseExists -PhaseName $FromPhase)) {
            $result.IsValid = $false
            $result.Errors += "Unknown from-phase: $FromPhase"
        }
        
        if (-not [string]::IsNullOrEmpty($ToPhase) -and -not (Test-PhaseExists -PhaseName $ToPhase)) {
            $result.IsValid = $false
            $result.Errors += "Unknown to-phase: $ToPhase"
        }
        
        # Validate range order
        if (-not [string]::IsNullOrEmpty($FromPhase) -and -not [string]::IsNullOrEmpty($ToPhase)) {
            $fromIndex = $validPhases.IndexOf($FromPhase)
            $toIndex = $validPhases.IndexOf($ToPhase)
            
            if ($fromIndex -gt $toIndex) {
                $result.IsValid = $false
                $result.Errors += "from-phase ($FromPhase) comes after to-phase ($ToPhase) in execution order"
            }
        }
    } else {
        $result.Warnings += "Cannot validate phase parameters - phase-functions.ps1 not found"
    }
    
    return $result
}

function Show-ValidationErrors {
    <#
    .SYNOPSIS
        Displays validation errors and warnings in a user-friendly format
    
    .PARAMETER ValidationResult
        The result from any validation function
    
    .PARAMETER ExitOnError
        Whether to exit the script if errors are found
    #>

    param(
        [Parameter(Mandatory)]
        [hashtable]$ValidationResult,
        
        [switch]$ExitOnError
    )
    
    # Show warnings
    if ($ValidationResult.Warnings) {
        foreach ($warning in $ValidationResult.Warnings) {
            Write-Warning $warning
        }
    }
    
    # Show deprecated parameters
    if ($ValidationResult.ContainsKey('DeprecatedParameters') -and $ValidationResult.DeprecatedParameters -and $ValidationResult.DeprecatedParameters.Count -gt 0) {
        Write-Warning "Deprecated parameters detected: $($ValidationResult.DeprecatedParameters -join ', ')"
    }
    
    # Show errors
    if ($ValidationResult.Errors) {
        foreach ($errorMsg in $ValidationResult.Errors) {
            Write-Error $errorMsg
        }
    }
    
    # Show suggestions if available
    if ($ValidationResult.ContainsKey('SuggestedNames') -and $ValidationResult.SuggestedNames -and $ValidationResult.SuggestedNames.Count -gt 0) {
        Write-Host "Suggestions: $($ValidationResult.SuggestedNames -join ', ')" -ForegroundColor Yellow
    }
    
    # Exit if requested and errors exist
    if ($ExitOnError -and -not $ValidationResult.IsValid) {
        Write-Error "Validation failed. Please correct the errors and try again."
        exit 1
    }
}