param(
    [ValidateSet("install", "check", "remove")]
    [string] $Mode = "install"
)

$ErrorActionPreference = "Stop"
$root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$client = Join-Path $root "repokey-client.cmd"
$manifestPath = Join-Path $root "repokey-mcp-config.json"
$serverName = "repokey-vivado"

function New-ServerEntry {
    return [pscustomobject]@{
        command = $client
        args = @("vivado", "serve")
    }
}

function Get-ConfigCandidates {
    $candidates = @()
    if ($env:APPDATA) {
        $claudePath = Join-Path $env:APPDATA "Claude\claude_desktop_config.json"
        $candidates += [pscustomobject]@{ Name = "Claude Desktop"; Kind = "json"; Path = $claudePath; DetectPath = (Split-Path -Parent $claudePath) }
        $clinePath = Join-Path $env:APPDATA "Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json"
        $candidates += [pscustomobject]@{ Name = "Cline"; Kind = "json"; Path = $clinePath; DetectPath = (Split-Path -Parent (Split-Path -Parent $clinePath)) }
    }
    if ($env:USERPROFILE) {
        $cursorPath = Join-Path $env:USERPROFILE ".cursor\mcp.json"
        $candidates += [pscustomobject]@{ Name = "Cursor"; Kind = "json"; Path = $cursorPath; DetectPath = (Split-Path -Parent $cursorPath) }
        $windsurfPath = Join-Path $env:USERPROFILE ".codeium\windsurf\mcp_config.json"
        $candidates += [pscustomobject]@{ Name = "Windsurf"; Kind = "json"; Path = $windsurfPath; DetectPath = (Split-Path -Parent $windsurfPath) }
        $claudeCodePath = Join-Path $env:USERPROFILE ".claude.json"
        $candidates += [pscustomobject]@{ Name = "Claude Code"; Kind = "json"; Path = $claudeCodePath; DetectPath = (Join-Path $env:USERPROFILE ".claude") }
        $codexPath = Join-Path $env:USERPROFILE ".codex\config.toml"
        $candidates += [pscustomobject]@{ Name = "Codex"; Kind = "toml"; Path = $codexPath; DetectPath = (Join-Path $env:USERPROFILE ".codex") }
    }
    return $candidates
}

function Test-CandidateInstalled {
    param([pscustomobject] $Candidate)
    return (Test-Path -LiteralPath $Candidate.Path) -or (Test-Path -LiteralPath $Candidate.DetectPath)
}

function Get-ServerContainer {
    param([psobject] $Config)
    foreach ($propertyName in @("mcpServers", "servers", "mcp_servers")) {
        $property = $Config.PSObject.Properties[$propertyName]
        if ($null -ne $property -and $null -ne $property.Value) {
            return [pscustomobject]@{ Name = $propertyName; Value = $property.Value }
        }
    }

    $container = [pscustomobject]@{}
    $Config | Add-Member -NotePropertyName "mcpServers" -NotePropertyValue $container
    return [pscustomobject]@{ Name = "mcpServers"; Value = $container }
}

function Get-ExistingServer {
    param([psobject] $Container)
    $property = $Container.PSObject.Properties[$serverName]
    if ($null -eq $property) { return $null }
    return $property.Value
}

function Test-SameServer {
    param([psobject] $Entry)
    if ($null -eq $Entry) { return $false }
    $entryCommand = [string]$Entry.command
    $entryArgs = @($Entry.args | ForEach-Object { [string]$_ })
    return ($entryCommand -eq $client) -and ($entryArgs -join "`n" -eq "vivado`nserve")
}

function Write-Manifest {
    $manifest = [ordered]@{
        name = $serverName
        command = $client
        args = @("vivado", "serve")
        transport = "stdio"
        note = "Register this server once in the AI client's MCP configuration."
    }
    $manifest | ConvertTo-Json -Depth 5 | Set-Content -Encoding UTF8 -LiteralPath $manifestPath
}

function Backup-Config {
    param([string] $Path)
    $stamp = Get-Date -Format "yyyyMMdd-HHmmss"
    $backup = "$Path.repokey.$stamp.bak"
    Copy-Item -LiteralPath $Path -Destination $backup -Force
    return $backup
}

function Update-JsonConfig {
    param([pscustomobject] $Candidate)
    $path = $Candidate.Path
    $config = $null
    if (Test-Path -LiteralPath $path) {
        try {
            $config = Get-Content -Raw -LiteralPath $path | ConvertFrom-Json
        } catch {
            Write-Host "[WARN] $($Candidate.Name) config is not valid JSON; skipped: $path"
            return "invalid"
        }
    } else {
        $parent = Split-Path -Parent $path
        New-Item -ItemType Directory -Force -Path $parent | Out-Null
        $config = [pscustomobject]@{}
    }

    $containerInfo = Get-ServerContainer $config
    $existing = Get-ExistingServer $containerInfo.Value
    if (Test-SameServer $existing) {
        Write-Host "[OK] $($Candidate.Name) already has RepoKey MCP configured."
        return "already"
    }

    if (Test-Path -LiteralPath $path) {
        $backup = Backup-Config $path
        Write-Host "[OK] Backed up $($Candidate.Name) config: $backup"
    }
    $containerInfo.Value | Add-Member -Force -NotePropertyName $serverName -NotePropertyValue (New-ServerEntry)
    $config | ConvertTo-Json -Depth 12 | Set-Content -Encoding UTF8 -LiteralPath $path
    Write-Host "[OK] Registered RepoKey MCP for $($Candidate.Name)."
    return "updated"
}

function Get-CodexBlock {
    param([string] $Text)
    $pattern = '(?ms)^\[mcp_servers\.(?:repokey_vivado|"repokey-vivado")\]\s*$.*?(?=^\[|\z)'
    return [regex]::Match($Text, $pattern).Value
}

function Test-SameCodexServer {
    param([string] $Block)
    if ([string]::IsNullOrWhiteSpace($Block)) { return $false }
    $normalizedClient = [regex]::Escape($client).Replace('\\', '[\\/]')
    $commandPattern = 'command\s*=\s*[''\"]cmd[''\"]'
    $argPattern = '[''\"]/?c[''\"]'
    return ($Block -match $commandPattern) -and
        ($Block -match $argPattern) -and
        ($Block -match $normalizedClient) -and
        ($Block -match "vivado") -and
        ($Block -match "serve")
}

function Update-CodexConfig {
    param([pscustomobject] $Candidate)
    $path = $Candidate.Path
    $text = ""
    if (Test-Path -LiteralPath $path) {
        $text = [System.IO.File]::ReadAllText($path)
    } else {
        New-Item -ItemType Directory -Force -Path (Split-Path -Parent $path) | Out-Null
    }

    $existingBlock = Get-CodexBlock $text
    if (Test-SameCodexServer $existingBlock) {
        Write-Host "[OK] Codex already has RepoKey MCP configured."
        return "already"
    }

    if (Test-Path -LiteralPath $path) {
        $backup = Backup-Config $path
        Write-Host "[OK] Backed up Codex config: $backup"
    }

    if (-not [string]::IsNullOrWhiteSpace($existingBlock)) {
        $text = $text.Replace($existingBlock, "").TrimEnd()
    }

    $tomlClient = $client.Replace('\\', '/')
    $block = @"

[mcp_servers.repokey_vivado]
command = 'cmd'
args = ['/c', '$tomlClient', 'vivado', 'serve']
startup_timeout_sec = 120
"@
    $utf8 = New-Object System.Text.UTF8Encoding($false)
    [System.IO.File]::WriteAllText($path, $text.TrimEnd() + $block + [Environment]::NewLine, $utf8)
    Write-Host "[OK] Registered RepoKey MCP for Codex."
    return "updated"
}

function Check-CodexConfig {
    param([pscustomobject] $Candidate)
    if (-not (Test-Path -LiteralPath $Candidate.Path)) {
        Write-Host "[INFO] Codex: configuration not created yet."
        return
    }
    $block = Get-CodexBlock ([System.IO.File]::ReadAllText($Candidate.Path))
    if (Test-SameCodexServer $block) {
        Write-Host "[OK] Codex: RepoKey MCP is registered."
    } else {
        Write-Host "[INFO] Codex: RepoKey MCP is not registered."
    }
}

function Remove-CodexConfig {
    param([pscustomobject] $Candidate)
    if (-not (Test-Path -LiteralPath $Candidate.Path)) { return }
    $text = [System.IO.File]::ReadAllText($Candidate.Path)
    $block = Get-CodexBlock $text
    if ([string]::IsNullOrWhiteSpace($block)) { return }
    $backup = Backup-Config $Candidate.Path
    $utf8 = New-Object System.Text.UTF8Encoding($false)
    [System.IO.File]::WriteAllText($Candidate.Path, $text.Replace($block, "").TrimEnd() + [Environment]::NewLine, $utf8)
    Write-Host "[OK] Removed RepoKey MCP from Codex; backup: $backup"
}

Write-Manifest
Write-Host "RepoKey MCP setup"
Write-Host "[OK] Registration manifest: $manifestPath"

$candidates = @(Get-ConfigCandidates | Where-Object { Test-CandidateInstalled $_ })
if ($candidates.Count -eq 0) {
    Write-Host "[INFO] No supported AI client configuration was detected."
    Write-Host "[INFO] The manifest can be used by an MCP-compatible AI client."
    exit 0
}

if ($Mode -eq "check") {
    foreach ($candidate in $candidates) {
        if ($candidate.Kind -eq "toml") {
            Check-CodexConfig $candidate
            continue
        }
        if (-not (Test-Path -LiteralPath $candidate.Path)) {
            Write-Host "[INFO] $($candidate.Name): configuration not created yet."
            continue
        }
        try {
            $config = Get-Content -Raw -LiteralPath $candidate.Path | ConvertFrom-Json
            $containerInfo = Get-ServerContainer $config
            if (Test-SameServer (Get-ExistingServer $containerInfo.Value)) {
                Write-Host "[OK] $($candidate.Name): RepoKey MCP is registered."
            } else {
                Write-Host "[INFO] $($candidate.Name): RepoKey MCP is not registered."
            }
        } catch {
            Write-Host "[WARN] $($candidate.Name): invalid or unsupported JSON config."
        }
    }
    exit 0
}

if ($Mode -eq "remove") {
    foreach ($candidate in $candidates) {
        if ($candidate.Kind -eq "toml") {
            Remove-CodexConfig $candidate
            continue
        }
        if (-not (Test-Path -LiteralPath $candidate.Path)) { continue }
        try {
            $config = Get-Content -Raw -LiteralPath $candidate.Path | ConvertFrom-Json
            $containerInfo = Get-ServerContainer $config
            $property = $containerInfo.Value.PSObject.Properties[$serverName]
            if ($null -ne $property) {
                $backup = Backup-Config $candidate.Path
                $containerInfo.Value.PSObject.Properties.Remove($serverName)
                $config | ConvertTo-Json -Depth 12 | Set-Content -Encoding UTF8 -LiteralPath $candidate.Path
                Write-Host "[OK] Removed RepoKey MCP from $($candidate.Name); backup: $backup"
            }
        } catch {
            Write-Host "[WARN] Could not update $($candidate.Name) config."
        }
    }
    exit 0
}

foreach ($candidate in $candidates) {
    if ($candidate.Kind -eq "toml") {
        [void](Update-CodexConfig $candidate)
    } else {
        [void](Update-JsonConfig $candidate)
    }
}

Write-Host "[OK] MCP registration check finished."
Write-Host "[INFO] Restart the AI client once so it reloads MCP configuration."
