Private/Config.ps1

$Script:ConfigPath = "$env:USERPROFILE\.pwshcopilot_config.json"
$ConfigPath = $Script:ConfigPath

function Initialize-PwshCopilot {
    param([switch]$Force)
    # Ensure config exists and has required provider-specific fields
    $needsConfig = $true
    if (-not $Force) {
        if (Test-Path $ConfigPath) {
            try {
                $config = Get-Content -Raw -Path $ConfigPath | ConvertFrom-Json
                # Back-compat: if no Provider, assume AzureOpenAI
                if (-not $config.Provider) { $config | Add-Member -NotePropertyName Provider -NotePropertyValue 'AzureOpenAI' -Force }

                $isValid = $false
                switch ($config.Provider) {
                    'OpenAI' {
                        if ($config.ApiKey -and $config.Model) { $isValid = $true }
                    }
                    'Claude' {
                        if ($config.ApiKey -and $config.Model -and $config.AnthropicVersion) { $isValid = $true }
                    }
                    default { # AzureOpenAI
                        if ($config.Endpoint -and $config.ApiKey -and $config.ApiVersion -and $config.Deployment) { $isValid = $true }
                    }
                }

                if ($isValid) { $needsConfig = $false }
            }
            catch {
                $needsConfig = $true
            }
        }
    }

    if ($needsConfig) {
        Write-Host "Welcome to PwshCopilot! Let's set up your LLM connection..." -ForegroundColor Cyan
        Write-Host "Choose provider:" -ForegroundColor Cyan
        Write-Host " 1) Azure OpenAI" -ForegroundColor Cyan
        Write-Host " 2) OpenAI" -ForegroundColor Cyan
        Write-Host " 3) Claude (Anthropic)" -ForegroundColor Cyan
        $choice = Read-Host "Enter 1, 2, or 3"
        switch ($choice) {
            '2' { $provider = 'OpenAI' }
            '3' { $provider = 'Claude' }
            default { $provider = 'AzureOpenAI' }
        }

        if ($Force -and (Test-Path $ConfigPath)) {
            Write-Host "Overwriting existing configuration at $ConfigPath" -ForegroundColor Yellow
        }

        switch ($provider) {
            'OpenAI' {
                $apikey = Read-Host "Enter your OpenAI API key"
                $model = Read-Host "Enter the OpenAI model (e.g., gpt-4o-mini)"
                if ([string]::IsNullOrWhiteSpace($model)) { $model = 'gpt-4o-mini' }
                $config = @{
                    Provider = 'OpenAI'
                    ApiKey   = $apikey
                    Model    = $model
                    # BaseUrl optional: default https://api.openai.com/v1
                }
            }
            'Claude' {
                $apikey = Read-Host "Enter your Anthropic API key"
                $model = Read-Host "Enter the Claude model (e.g., claude-3-5-sonnet-20240620)"
                if ([string]::IsNullOrWhiteSpace($model)) { $model = 'claude-3-5-sonnet-20240620' }
                $anthVer = Read-Host "Enter Anthropic API version (default: 2023-06-01)"
                if ([string]::IsNullOrWhiteSpace($anthVer)) { $anthVer = '2023-06-01' }
                $config = @{
                    Provider          = 'Claude'
                    ApiKey            = $apikey
                    Model             = $model
                    AnthropicVersion  = $anthVer
                    # BaseUrl optional: default https://api.anthropic.com/v1
                }
            }
            default { # AzureOpenAI
                $endpoint = Read-Host "Enter your Azure OpenAI endpoint (e.g., https://your-resource-name.openai.azure.com)"
                $apikey = Read-Host "Enter your API key"
                $apiversion = Read-Host "Enter your API version (e.g., 2024-12-01-preview)"
                $deployment = Read-Host "Enter your deployment name"

                # Normalize endpoint by trimming trailing slashes
                if ($endpoint) { $endpoint = $endpoint.TrimEnd('/') }

                $config = @{
                    Provider  = 'AzureOpenAI'
                    Endpoint  = $endpoint
                    ApiKey    = $apikey
                    ApiVersion = $apiversion
                    Deployment = $deployment
                }
            }
        }

        $config | ConvertTo-Json | Set-Content -Path $ConfigPath
        Write-Host "Configuration saved to $ConfigPath" -ForegroundColor Green
    }
}

function Get-PSCopilotConfig {
    if (-not (Test-Path $ConfigPath)) {
        Initialize-PwshCopilot
    }

    try {
        $config = Get-Content -Raw -Path $ConfigPath | ConvertFrom-Json
    }
    catch {
        Write-Host "Existing configuration file is invalid JSON. Re-initializing..." -ForegroundColor Yellow
        Initialize-PwshCopilot
        $config = Get-Content -Raw -Path $ConfigPath | ConvertFrom-Json
    }

    # Back-compat: default to Azure if Provider missing
    if (-not $config.Provider) { $config | Add-Member -NotePropertyName Provider -NotePropertyValue 'AzureOpenAI' -Force }

    $missing = @()
    switch ($config.Provider) {
        'OpenAI' {
            if ([string]::IsNullOrWhiteSpace($config.ApiKey)) { $missing += 'ApiKey' }
            if ([string]::IsNullOrWhiteSpace($config.Model)) { $missing += 'Model' }
        }
        'Claude' {
            if ([string]::IsNullOrWhiteSpace($config.ApiKey)) { $missing += 'ApiKey' }
            if ([string]::IsNullOrWhiteSpace($config.Model)) { $missing += 'Model' }
            if ([string]::IsNullOrWhiteSpace($config.AnthropicVersion)) { $missing += 'AnthropicVersion' }
        }
        default { # AzureOpenAI
            if ([string]::IsNullOrWhiteSpace($config.Endpoint)) { $missing += 'Endpoint' }
            if ([string]::IsNullOrWhiteSpace($config.ApiKey)) { $missing += 'ApiKey' }
            if ([string]::IsNullOrWhiteSpace($config.ApiVersion)) { $missing += 'ApiVersion' }
            if ([string]::IsNullOrWhiteSpace($config.Deployment)) { $missing += 'Deployment' }
        }
    }

    if ($missing.Count -gt 0) {
        Write-Host ("Configuration missing fields: {0}. Launching setup..." -f ($missing -join ', ')) -ForegroundColor Yellow
        Initialize-PwshCopilot
        $config = Get-Content -Raw -Path $ConfigPath | ConvertFrom-Json
    }

    return $config
}

if ($MyInvocation.InvocationName -ne '.') {
    Initialize-PwshCopilot -Force
}

<#
VOICE CONFIGURATION
Adds parallel setup for Azure Speech (STT/TTS) similar to LLM provider configuration.
Persists additional JSON keys in the existing config file:
  SpeechProvider (currently only 'AzureSpeech')
  SpeechKey
  SpeechRegion
  SpeechVoice (optional, default en-US-JennyNeural)
Use Initialize-PwshCopilotVoice (or alias Initialize-VoiceCopilot) to configure / reconfigure.
Get-PSCopilotVoiceConfig returns a validated hashtable or triggers setup if missing.
#>


function Initialize-PwshCopilotVoice {
    [CmdletBinding()] param([switch]$Force)

    $config = $null
    if (Test-Path $ConfigPath) {
        try { $config = Get-Content -Raw -Path $ConfigPath | ConvertFrom-Json } catch { $config = [pscustomobject]@{} }
    } else { $config = [pscustomobject]@{} }

    $needsConfig = $Force
    if (-not $needsConfig) {
        if (-not $config.SpeechProvider) { $needsConfig = $true }
        elseif ($config.SpeechProvider -eq 'AzureSpeech' -and (-not $config.SpeechKey -or -not $config.SpeechRegion)) { $needsConfig = $true }
        elseif ($config.SpeechProvider -eq 'AzureOpenAIWhisper' -and (-not $config.WhisperEndpoint -or -not $config.WhisperApiKey -or -not $config.WhisperDeployment -or -not $config.WhisperApiVersion)) { $needsConfig = $true }
    }

    if (-not $needsConfig) { Write-Verbose "Voice configuration already present. Use -Force to reconfigure."; return }

    Write-Host "Select voice / transcription provider:" -ForegroundColor Cyan
    Write-Host " 1) Azure Speech (legacy STT/TTS)" -ForegroundColor Cyan
    Write-Host " 2) Azure OpenAI Whisper (recommended)" -ForegroundColor Cyan
    $choice = Read-Host "Enter 1 or 2"
    switch ($choice) {
        '2' { $provider = 'AzureOpenAIWhisper' }
        default { $provider = 'AzureSpeech' }
    }

    if ($provider -eq 'AzureSpeech') {
        $speechKey = Read-Host "Enter your Azure Speech key"
        $speechRegion = Read-Host "Enter your Azure Speech region (e.g., eastus)"
        $speechVoice = Read-Host "Enter default voice (blank for en-US-JennyNeural)"
        if ([string]::IsNullOrWhiteSpace($speechVoice)) { $speechVoice = 'en-US-JennyNeural' }
        $config | Add-Member -NotePropertyName SpeechProvider -NotePropertyValue $provider -Force
        $config | Add-Member -NotePropertyName SpeechKey -NotePropertyValue $speechKey -Force
        $config | Add-Member -NotePropertyName SpeechRegion -NotePropertyValue $speechRegion -Force
        $config | Add-Member -NotePropertyName SpeechVoice -NotePropertyValue $speechVoice -Force
        # Clean Whisper keys if switching
        'WhisperEndpoint','WhisperApiKey','WhisperDeployment','WhisperApiVersion' | ForEach-Object { if ($config.PSObject.Properties.Name -contains $_) { $config.PSObject.Properties.Remove($_) } }
    }
    else { # AzureOpenAIWhisper
        $wEndpoint = Read-Host "Enter Whisper Azure OpenAI endpoint (e.g., https://your-resource.openai.azure.com)"
        if ($wEndpoint) { $wEndpoint = $wEndpoint.TrimEnd('/') }
        $wKey = Read-Host "Enter Whisper Azure OpenAI API key"
        $wDeployment = Read-Host "Enter Whisper deployment name (default: whisper)"
        if ([string]::IsNullOrWhiteSpace($wDeployment)) { $wDeployment = 'whisper' }
        $wApiVersion = Read-Host "Enter Whisper API version (default: 2023-09-01-preview)"
        if ([string]::IsNullOrWhiteSpace($wApiVersion)) { $wApiVersion = '2023-09-01-preview' }
        $config | Add-Member -NotePropertyName SpeechProvider -NotePropertyValue $provider -Force
        $config | Add-Member -NotePropertyName WhisperEndpoint -NotePropertyValue $wEndpoint -Force
        $config | Add-Member -NotePropertyName WhisperApiKey -NotePropertyValue $wKey -Force
        $config | Add-Member -NotePropertyName WhisperDeployment -NotePropertyValue $wDeployment -Force
        $config | Add-Member -NotePropertyName WhisperApiVersion -NotePropertyValue $wApiVersion -Force
        # Remove legacy speech fields if present (except SpeechProvider)
        'SpeechKey','SpeechRegion','SpeechVoice' | ForEach-Object { if ($config.PSObject.Properties.Name -contains $_) { $config.PSObject.Properties.Remove($_) } }
    }

    $config | ConvertTo-Json | Set-Content -Path $ConfigPath
    Write-Host "Voice/transcription configuration saved to $ConfigPath" -ForegroundColor Green
}

Set-Alias -Name Initialize-VoiceCopilot -Value Initialize-PwshCopilotVoice

function Get-PSCopilotVoiceConfig {
    $cfg = Get-PSCopilotConfig
    $missing = @()
    if (-not $cfg.SpeechProvider) { $missing += 'SpeechProvider' }
    elseif ($cfg.SpeechProvider -eq 'AzureSpeech') {
        if (-not $cfg.SpeechKey) { $missing += 'SpeechKey' }
        if (-not $cfg.SpeechRegion) { $missing += 'SpeechRegion' }
    }
    elseif ($cfg.SpeechProvider -eq 'AzureOpenAIWhisper') {
        foreach ($f in 'WhisperEndpoint','WhisperApiKey','WhisperDeployment','WhisperApiVersion') { if (-not $cfg.$f) { $missing += $f } }
    }
    if ($missing.Count -gt 0) {
        Write-Host ("Voice configuration missing fields: {0}. Launching setup..." -f ($missing -join ', ')) -ForegroundColor Yellow
        Initialize-PwshCopilotVoice
        $cfg = Get-Content -Raw -Path $ConfigPath | ConvertFrom-Json
    }
    return $cfg
}

# Check and report missing prerequisites (ffmpeg, keys, etc.)
function Test-PSCopilotPrerequisites {
    [CmdletBinding()] param([switch]$Silent)
    $cfg = $null; try { $cfg = Get-PSCopilotConfig } catch {}
    $voice = $null; try { $voice = Get-PSCopilotVoiceConfig } catch {}

    $issues = @()
    $advise = @()

    $ffmpeg = Get-Command ffmpeg -ErrorAction SilentlyContinue
    if (-not $ffmpeg) {
        $issues += 'ffmpeg (required for microphone audio capture)'
        $advise += 'Install ffmpeg: winget install Gyan.FFmpeg OR choco install ffmpeg -y OR download: https://www.gyan.dev/ffmpeg/builds/'
    }

    if ($voice -and $voice.SpeechProvider -eq 'AzureOpenAIWhisper') {
        foreach ($f in 'WhisperEndpoint','WhisperApiKey','WhisperDeployment','WhisperApiVersion') {
            if (-not $voice.$f) { $issues += "Whisper field missing: $f" }
        }
    }
    elseif ($voice -and $voice.SpeechProvider -eq 'AzureSpeech') {
        foreach ($f in 'SpeechKey','SpeechRegion') { if (-not $voice.$f) { $issues += "Azure Speech field missing: $f" } }
    }

    if ($issues.Count -eq 0) {
        if (-not $Silent) { Write-Host 'PwshCopilot prerequisites: OK' -ForegroundColor Green }
        return $true
    }
    if (-not $Silent) {
        Write-Warning ('Missing prerequisites: ' + ($issues -join '; '))
        foreach ($a in $advise) { Write-Host $a -ForegroundColor Yellow }
    }
    return $false
}