src/PowerShell/Config.ps1

# cdp PowerShell domain: Config.ps1
# Loaded by src/cdp.psm1; do not dot-source peer files.

function Resolve-CdpFzfCommand {
    if (-not [string]::IsNullOrWhiteSpace($env:CDP_FZF_PATH)) {
        $configuredPath = [Environment]::ExpandEnvironmentVariables($env:CDP_FZF_PATH)
        if (Test-Path -LiteralPath $configuredPath -PathType Leaf) {
            $script:CdpFzfCommand = (Get-Item -LiteralPath $configuredPath).FullName
            return $script:CdpFzfCommand
        }
    }

    if (-not [string]::IsNullOrWhiteSpace($script:CdpFzfCommand) -and
        (Test-Path -LiteralPath $script:CdpFzfCommand -PathType Leaf)) {
        return $script:CdpFzfCommand
    }

    $fzfCommand = Get-Command fzf -ErrorAction SilentlyContinue | Select-Object -First 1
    if ($fzfCommand) {
        $script:CdpFzfCommand = if ([string]::IsNullOrWhiteSpace($fzfCommand.Path)) {
            $fzfCommand.Name
        } else {
            $fzfCommand.Path
        }
        return $script:CdpFzfCommand
    }

    return $null
}

function Clear-CdpProjectConfigCache {
    param(
        [Parameter(Mandatory = $false)]
        [string]$ConfigPath
    )

    if ([string]::IsNullOrWhiteSpace($ConfigPath)) {
        $script:CdpProjectConfigCache.Clear()
        return
    }

    try {
        $cacheKey = (Get-Item -LiteralPath $ConfigPath -ErrorAction Stop).FullName
        [void]$script:CdpProjectConfigCache.Remove($cacheKey)
    } catch {
        $script:CdpProjectConfigCache.Clear()
    }
}

function Get-CdpProjectConfig {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ConfigPath
    )

    $configItem = Get-Item -LiteralPath $ConfigPath -ErrorAction Stop
    $cacheKey = $configItem.FullName
    $cachedConfig = $script:CdpProjectConfigCache[$cacheKey]

    if ($cachedConfig -and
        $cachedConfig.Length -eq $configItem.Length -and
        $cachedConfig.LastWriteTimeUtcTicks -eq $configItem.LastWriteTimeUtc.Ticks) {
        return $cachedConfig
    }

    $jsonContent = Get-Content -LiteralPath $configItem.FullName -Raw -Encoding UTF8
    $allProjects = ConvertFrom-Json -InputObject $jsonContent
    $projects = (ConvertTo-CdpJsonArrayValue -Value $allProjects).Value

    $configData = [PSCustomObject]@{
        Path = $configItem.FullName
        Length = $configItem.Length
        LastWriteTimeUtcTicks = $configItem.LastWriteTimeUtc.Ticks
        Projects = $projects
        EnabledProjects = @($projects | Where-Object { $_.enabled })
    }

    $script:CdpProjectConfigCache[$cacheKey] = $configData
    return $configData
}

function Get-CdpProjectMatches {
    param(
        [Parameter(Mandatory = $true)]
        [object[]]$Projects,

        [Parameter(Mandatory = $true)]
        [string]$Query
    )

    $comparison = [StringComparison]::OrdinalIgnoreCase

    if ($Query.StartsWith('@')) {
        $tagQuery = $Query.Substring(1)
        return @($Projects | Where-Object {
            @(Get-CdpProjectStringList -Project $_ -PropertyName 'tags') |
                Where-Object { [string]::Equals($_, $tagQuery, $comparison) }
        })
    }

    $exactMatches = @($Projects | Where-Object {
        $projectAliases = @(Get-CdpProjectStringList -Project $_ -PropertyName 'aliases')
        [string]::Equals([string]$_.name, $Query, $comparison) -or
            @($projectAliases | Where-Object { [string]::Equals($_, $Query, $comparison) }).Count -gt 0
    })

    if ($exactMatches.Count -gt 0) {
        return $exactMatches
    }

    return @($Projects | Where-Object {
        $projectName = if ($null -eq $_.name) { "" } else { [string]$_.name }
        $projectPath = if ($null -eq $_.rootPath) { "" } else { [string]$_.rootPath }
        $profilePaths = @()
        if ($_.PSObject.Properties['paths'] -and $null -ne $_.paths) {
            $profilePaths = @($_.paths.PSObject.Properties | Where-Object {
                $_.Value -is [string]
            } | ForEach-Object { [string]$_.Value })
        }
        $projectAliases = @(Get-CdpProjectStringList -Project $_ -PropertyName 'aliases')
        $projectTags = @(Get-CdpProjectStringList -Project $_ -PropertyName 'tags')

        $projectName.IndexOf($Query, $comparison) -ge 0 -or
            $projectPath.IndexOf($Query, $comparison) -ge 0 -or
            @($profilePaths | Where-Object { $_.IndexOf($Query, $comparison) -ge 0 }).Count -gt 0 -or
            @($projectAliases | Where-Object { $_.IndexOf($Query, $comparison) -ge 0 }).Count -gt 0 -or
            @($projectTags | Where-Object { $_.IndexOf($Query, $comparison) -ge 0 }).Count -gt 0
    })
}

function Test-CdpConfigPathArgument {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Argument
    )

    if ([string]::IsNullOrWhiteSpace($Argument)) {
        return $false
    }

    if (Test-Path -LiteralPath $Argument) {
        return $true
    }

    return $Argument.EndsWith(".json", [StringComparison]::OrdinalIgnoreCase) -or
        $Argument.Contains('\') -or
        $Argument.Contains('/') -or
        $Argument.StartsWith('~') -or
        $Argument -match '^[A-Za-z]:'
}

function Get-StoredConfigChoice {
    $configChoiceFile = Join-Path (Get-CdpUserHome) ".cdp\config"
    if (Test-Path $configChoiceFile) {
        $storedPath = Get-Content -Path $configChoiceFile -Raw -ErrorAction SilentlyContinue
        if (-not [string]::IsNullOrWhiteSpace($storedPath)) {
            return $storedPath.Trim()
        }
    }
    return $null
}

function Save-ConfigChoice {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ConfigPath
    )

    if ($WhatIfPreference) {
        Write-Host "Would save active config choice: $ConfigPath" -ForegroundColor Gray
        return
    }

    $configChoiceFile = Join-Path (Get-CdpUserHome) ".cdp\config"
    $configDir = Split-Path -Parent $configChoiceFile

    if (-not (Test-Path $configDir)) {
        New-Item -ItemType Directory -Path $configDir -Force | Out-Null
    }

    $ConfigPath | Out-File -FilePath $configChoiceFile -Encoding UTF8 -NoNewline
}

function Get-AllAvailableConfigs {
    $configs = @()

    # Check all possible locations
    $cursorPath = Join-Path $env:APPDATA "Cursor\User\globalStorage\alefragnani.project-manager\projects.json"
    $vscodePath = Join-Path $env:APPDATA "Code\User\globalStorage\alefragnani.project-manager\projects.json"
    $customConfigPath = Join-Path (Get-CdpUserHome) ".cdp\projects.json"

    if (Test-Path $cursorPath) {
        $configs += [PSCustomObject]@{
            Path = $cursorPath
            Source = "Cursor Project Manager"
        }
    }

    if (Test-Path $vscodePath) {
        $configs += [PSCustomObject]@{
            Path = $vscodePath
            Source = "VS Code Project Manager"
        }
    }

    if (Test-Path $customConfigPath) {
        $configs += [PSCustomObject]@{
            Path = $customConfigPath
            Source = "Custom Config (~/.cdp)"
        }
    }

    return $configs
}

function Get-DefaultConfigPath {
    # Priority order:
    # 1. Environment variable (highest priority, skip selection)
    # 2. Stored user choice from previous selection (~/.cdp/config)
    # 3. If multiple configs exist, let user choose and save choice
    # 4. Otherwise return the first available or default path

    if (-not [string]::IsNullOrWhiteSpace($env:CDP_CONFIG)) {
        return $env:CDP_CONFIG
    }

    # Check for stored config choice
    $storedChoice = Get-StoredConfigChoice
    if ($storedChoice -and (Test-Path $storedChoice)) {
        return $storedChoice
    }

    # Find all available configs
    $availableConfigs = Get-AllAvailableConfigs

    # If no configs found, return default (will be created)
    if ($availableConfigs.Count -eq 0) {
        $customConfigPath = Join-Path (Get-CdpUserHome) ".cdp\projects.json"
        return $customConfigPath
    }

    # If only one config, use it without mutating the active-choice file.
    if ($availableConfigs.Count -eq 1) {
        return $availableConfigs[0].Path
    }

    # Multiple configs found - let user choose
    Write-Host "`nMultiple configuration files found:" -ForegroundColor Cyan
    Write-Host ("=" * 80) -ForegroundColor Gray
    Write-Host ""

    for ($i = 0; $i -lt $availableConfigs.Count; $i++) {
        $config = $availableConfigs[$i]
        Write-Host " [$($i + 1)] " -ForegroundColor Yellow -NoNewline
        Write-Host "$($config.Source)" -ForegroundColor Green
        Write-Host " $($config.Path)" -ForegroundColor Gray
    }

    Write-Host ""
    Write-Host "Use " -ForegroundColor Gray -NoNewline
    Write-Host "cdp-config" -ForegroundColor Cyan -NoNewline
    Write-Host " to persist an active configuration choice." -ForegroundColor Gray
    Write-Host "Or set " -ForegroundColor Gray -NoNewline
    Write-Host "`$env:CDP_CONFIG" -ForegroundColor Cyan -NoNewline
    Write-Host " to override." -ForegroundColor Gray
    Write-Host ""

    # Get user selection
    do {
        $selection = Read-Host "Select config file (1-$($availableConfigs.Count))"
        $selectedIndex = $null
        if ([int]::TryParse($selection, [ref]$selectedIndex)) {
            if ($selectedIndex -ge 1 -and $selectedIndex -le $availableConfigs.Count) {
                $selectedPath = $availableConfigs[$selectedIndex - 1].Path
                $selectedSource = $availableConfigs[$selectedIndex - 1].Source

                Write-Host "`nUsing: $selectedSource" -ForegroundColor Green
                Write-Host "Path: $selectedPath" -ForegroundColor Gray
                Write-Host "Choice not persisted; use cdp-config to save it." -ForegroundColor Gray
                Write-Host ""
                return $selectedPath
            }
        }
        Write-Host "Invalid selection. Please enter a number between 1 and $($availableConfigs.Count)." -ForegroundColor Red
    } while ($true)
}

function Initialize-ConfigFile {
    param(
        [string]$ConfigPath
    )

    if (-not (Test-Path $ConfigPath)) {
        if ($WhatIfPreference) {
            Write-Host "Would create new config file at: $ConfigPath" -ForegroundColor Gray
            return
        }
        [void](Write-CdpJsonFile -LiteralPath $ConfigPath -Value @() -ExpectedFingerprint 'missing')
        Write-Host "Created new config file at: $ConfigPath" -ForegroundColor Green
    }
}