$ErrorActionPreference = 'Stop'

[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

$Repo = 'codeasier/mtls-router'
$ScriptDir = $PSScriptRoot
$InstallDir = if ($env:MTLS_ROUTER_INSTALL_DIR) { $env:MTLS_ROUTER_INSTALL_DIR } else { Join-Path $env:USERPROFILE '.local\bin' }
$StateDir = if ($env:MTLS_ROUTER_STATE_DIR) { $env:MTLS_ROUTER_STATE_DIR } else { Join-Path $env:USERPROFILE '.mtls-router' }
$DefaultDownloadBaseUrl = 'https://downloads.codeasier.top/mtls-router/v0.1.8'
$DownloadBaseUrl = if ($env:MTLS_ROUTER_DOWNLOAD_URL) { $env:MTLS_ROUTER_DOWNLOAD_URL } else { $DefaultDownloadBaseUrl }
$DownloadUser = $env:MTLS_ROUTER_DOWNLOAD_USER
$DownloadPassword = $env:MTLS_ROUTER_DOWNLOAD_PASSWORD
$AllowDownload = $env:MTLS_ROUTER_ALLOW_DOWNLOAD -eq '1'
$RouterPath = Join-Path $InstallDir 'mtls-router.exe'
$ManagerPath = Join-Path $InstallDir 'mtls-router-manager.exe'
$ReceiptPath = Join-Path $StateDir 'install-receipt.json'
$PendingPath = Join-Path $StateDir 'install-pending.json'
$BackupDir = Join-Path $StateDir 'install-previous'
$RouterBaseUrl = 'http://127.0.0.1:19099'

function Write-Info($Message) { Write-Host $Message -ForegroundColor Cyan }
function Write-Success($Message) { Write-Host $Message -ForegroundColor Green }
function Write-Warn($Message) { Write-Host $Message -ForegroundColor Yellow }
function Write-Fail($Message) { Write-Host $Message -ForegroundColor Red -ErrorAction Continue; exit 1 }

function Show-Banner {
    Write-Info '============================================================'
    Write-Info ' mtls-router 代理配置向导'
    Write-Info '============================================================'
}

function Get-PlatformAssets {
    $arch = if (($env:PROCESSOR_ARCHITECTURE -match 'ARM64') -or ($env:PROCESSOR_ARCHITEW6432 -match 'ARM64')) { 'arm64' }
        elseif ($env:PROCESSOR_ARCHITECTURE -match 'AMD64|x86_64') { 'amd64' }
        else { Write-Fail "不支持的 CPU 架构：$env:PROCESSOR_ARCHITECTURE" }
    return [pscustomobject]@{
        Router = "mtls-router-windows-$arch.exe"
        Manager = "mtls-router-manager-windows-$arch.exe"
    }
}

function Get-ExpectedChecksum($ManifestPath, $Asset) {
    $escaped = [regex]::Escape($Asset)
    $candidates = @(Get-Content -LiteralPath $ManifestPath | Where-Object { $_ -match "[\s\*]$escaped$" })
    if ($candidates.Count -ne 1) { Write-Fail "SHA256SUMS 必须包含且仅包含一条 $Asset 校验候选记录。" }
    if ($candidates[0] -notmatch "^[0-9A-Fa-f]{64} ( |\*)$escaped$") { Write-Fail "SHA256SUMS 中的 $Asset 校验记录格式无效。" }
    return $candidates[0].Substring(0, 64).ToLowerInvariant()
}

function Get-Hash($Path) {
    return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}

function Test-Checksum($SourcePath, $ManifestPath, $Asset) {
    if ((Get-Hash $SourcePath) -ne (Get-ExpectedChecksum $ManifestPath $Asset)) { Write-Fail "SHA-256 校验失败：$Asset" }
}

function Test-Hash($Path, $Expected) {
    return (Test-Path -LiteralPath $Path -PathType Leaf) -and ((Get-Hash $Path) -eq $Expected)
}

function Set-PrivatePath($Path, [bool]$Directory) {
    if (-not $IsWindows -and $PSVersionTable.PSVersion.Major -ge 6) {
        & chmod $(if ($Directory) { '700' } else { '600' }) $Path
        return
    }
    try {
        $acl = Get-Acl -LiteralPath $Path
        $acl.SetAccessRuleProtection($true, $false)
        foreach ($rule in @($acl.Access)) { $acl.RemoveAccessRuleAll($rule) }
        $identity = [Security.Principal.WindowsIdentity]::GetCurrent().Name
        $rights = if ($Directory) { [Security.AccessControl.FileSystemRights]::FullControl } else { [Security.AccessControl.FileSystemRights]::FullControl }
        $inheritance = if ($Directory) { [Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit' } else { [Security.AccessControl.InheritanceFlags]::None }
        $rule = New-Object Security.AccessControl.FileSystemAccessRule($identity, $rights, $inheritance, [Security.AccessControl.PropagationFlags]::None, [Security.AccessControl.AccessControlType]::Allow)
        $acl.AddAccessRule($rule)
        Set-Acl -LiteralPath $Path -AclObject $acl
    } catch { Write-Fail "无法限制安装状态权限：$Path" }
}

function Write-PrivateJson($Path, $Value) {
    $dir = Split-Path -Parent $Path
    New-Item -ItemType Directory -Force -Path $dir | Out-Null
    Set-PrivatePath $dir $true
    $tmp = Join-Path $dir ('.' + [IO.Path]::GetFileName($Path) + '.' + [Guid]::NewGuid().ToString('N'))
    try {
        $bytes = [Text.UTF8Encoding]::new($false).GetBytes($Value + [Environment]::NewLine)
        $stream = [IO.File]::Open($tmp, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
        try { $stream.Write($bytes, 0, $bytes.Length); $stream.Flush($true) } finally { $stream.Dispose() }
        Set-PrivatePath $tmp $false
        Move-Item -LiteralPath $tmp -Destination $Path -Force
        Set-PrivatePath $Path $false
    } finally { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue }
}

function Test-Receipt {
    if (-not (Test-Path -LiteralPath $ReceiptPath -PathType Leaf)) { return $false }
    try { $receipt = Get-Content -LiteralPath $ReceiptPath -Raw | ConvertFrom-Json } catch { return $false }
    if ($receipt.schema_version -ne 1 -or $receipt.router.path -cne $RouterPath -or $receipt.manager.path -cne $ManagerPath) { return $false }
    if (-not $receipt.deployment_id -or -not $receipt.management_protocol_version) { return $false }
    return (Test-Hash $RouterPath $receipt.router.sha256) -and (Test-Hash $ManagerPath $receipt.manager.sha256)
}

function Commit-Pending {
    $pending = Get-Content -LiteralPath $PendingPath -Raw | ConvertFrom-Json
    $receipt = [ordered]@{
        schema_version = 1
        deployment_id = $pending.deployment_id
        management_protocol_version = $pending.management_protocol_version
        installed_at = $pending.installed_at
        router = $pending.router
        manager = $pending.manager
    } | ConvertTo-Json -Depth 20
    Write-PrivateJson $ReceiptPath $receipt
    Remove-Item -LiteralPath $PendingPath -Force
    Remove-Item -LiteralPath $BackupDir -Recurse -Force -ErrorAction SilentlyContinue
}

function Restore-Previous {
    $pending = Get-Content -LiteralPath $PendingPath -Raw | ConvertFrom-Json
    if ($pending.previous.committed) {
        $routerBackup = Join-Path $BackupDir 'mtls-router.exe'
        $managerBackup = Join-Path $BackupDir 'mtls-router-manager.exe'
        if (-not (Test-Hash $routerBackup $pending.previous.router.sha256) -or -not (Test-Hash $managerBackup $pending.previous.manager.sha256)) { return $false }
        Copy-Item -LiteralPath $routerBackup -Destination (Join-Path $InstallDir '.mtls-router.restore') -Force
        Copy-Item -LiteralPath $managerBackup -Destination (Join-Path $InstallDir '.mtls-router-manager.restore') -Force
        Move-Item -LiteralPath (Join-Path $InstallDir '.mtls-router.restore') -Destination $RouterPath -Force
        Move-Item -LiteralPath (Join-Path $InstallDir '.mtls-router-manager.restore') -Destination $ManagerPath -Force
        if (-not (Test-Hash $RouterPath $pending.previous.router.sha256) -or -not (Test-Hash $ManagerPath $pending.previous.manager.sha256)) { return $false }
        Write-PrivateJson $ReceiptPath ($pending.previous.receipt | ConvertTo-Json -Depth 20)
    } else {
        Remove-Item -LiteralPath $RouterPath, $ManagerPath, $ReceiptPath -Force -ErrorAction SilentlyContinue
        if ((Test-Path $RouterPath) -or (Test-Path $ManagerPath)) { return $false }
    }
    Remove-Item -LiteralPath $PendingPath -Force
    Remove-Item -LiteralPath $BackupDir -Recurse -Force -ErrorAction SilentlyContinue
    return $true
}

function Repair-PendingInstall {
    if (-not (Test-Path -LiteralPath $PendingPath)) { return }
    try { $pending = Get-Content -LiteralPath $PendingPath -Raw | ConvertFrom-Json } catch { Write-Fail "安装事务标记损坏，拒绝执行 router 或 manager：$PendingPath" }
    if ($pending.schema_version -ne 1 -or $pending.router.path -cne $RouterPath -or $pending.manager.path -cne $ManagerPath) { Write-Fail '安装事务标记与固定安装路径不匹配，拒绝执行。' }
    if ((Test-Hash $RouterPath $pending.router.sha256) -and (Test-Hash $ManagerPath $pending.manager.sha256)) {
        Commit-Pending
        Write-Success '  已完成中断的 mtls-router/manager 安装事务。'
        return
    }
    if (-not (Restore-Previous)) { Write-Fail '无法证明新旧任一完整安装代，拒绝执行 router 或 manager。' }
    Write-Warn '  已回滚中断的 mtls-router/manager 安装事务。'
}

function Invoke-ManagerAt($Manager, $Router, $Request) {
    $response = $Request | & $Manager serve --router-sidecar $Router
    if ($LASTEXITCODE -ne 0) { Write-Fail 'mtls-router-manager 执行失败。' }
    $lines = @($response)
    if ($lines.Count -ne 1) { Write-Fail 'manager 返回了无效的多行响应。' }
    try { $decoded = $lines[0] | ConvertFrom-Json } catch { Write-Fail 'manager 返回了无效 JSON。' }
    if ($decoded.error) { Write-Fail "manager $($decoded.error.code): $($decoded.error.message)" }
    return $decoded
}

function Get-BinaryVersion($Path) {
    $output = & $Path --version 2>$null
    if ($LASTEXITCODE -ne 0 -or -not $output) { Write-Fail "无法读取已验证二进制版本：$Path" }
    return ([string]$output).Split(' ')[-1]
}

function Install-Pair($SourceRouter, $SourceManager) {
    $routerHash = Get-Hash $SourceRouter
    $managerHash = Get-Hash $SourceManager
    $info = Invoke-ManagerAt $SourceManager $SourceRouter '{"id":"setup-info","method":"manager.info","params":{}}'
    if (-not $info.result.version -or -not $info.result.deployment_id -or -not $info.result.management_protocol_version) { Write-Fail 'manager 元数据响应无效。' }
    $routerVersion = Get-BinaryVersion $SourceRouter
    if ($routerVersion -cne [string]$info.result.version) { Write-Fail 'router 与 manager 版本不匹配，拒绝安装。' }
    New-Item -ItemType Directory -Force -Path $InstallDir, $StateDir | Out-Null
    Set-PrivatePath $StateDir $true
    Repair-PendingInstall

    Remove-Item -LiteralPath $BackupDir -Recurse -Force -ErrorAction SilentlyContinue
    New-Item -ItemType Directory -Path $BackupDir | Out-Null
    Set-PrivatePath $BackupDir $true
    $previous = $false
    $oldReceipt = [pscustomobject]@{}
    if ((Test-Path $RouterPath) -or (Test-Path $ManagerPath) -or (Test-Path $ReceiptPath)) {
        if (-not (Test-Receipt)) { Write-Fail '现有安装不是 receipt 验证的完整二进制对；拒绝部分覆盖。' }
        Copy-Item -LiteralPath $RouterPath -Destination (Join-Path $BackupDir 'mtls-router.exe')
        Copy-Item -LiteralPath $ManagerPath -Destination (Join-Path $BackupDir 'mtls-router-manager.exe')
        Set-PrivatePath (Join-Path $BackupDir 'mtls-router.exe') $false
        Set-PrivatePath (Join-Path $BackupDir 'mtls-router-manager.exe') $false
        $oldReceipt = Get-Content -LiteralPath $ReceiptPath -Raw | ConvertFrom-Json
        $previous = $true
    }
    $pending = [ordered]@{
        schema_version = 1
        transaction_id = [Guid]::NewGuid().ToString('N')
        installed_at = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
        deployment_id = $info.result.deployment_id
        management_protocol_version = $info.result.management_protocol_version
        router = [ordered]@{ path = $RouterPath; sha256 = $routerHash; version = $routerVersion }
        manager = [ordered]@{ path = $ManagerPath; sha256 = $managerHash; version = $info.result.version }
        previous = [ordered]@{
            committed = $previous
            receipt = $oldReceipt
            router = if ($previous) { $oldReceipt.router } else { [pscustomobject]@{} }
            manager = if ($previous) { $oldReceipt.manager } else { [pscustomobject]@{} }
        }
    } | ConvertTo-Json -Depth 20
    Write-PrivateJson $PendingPath $pending

    Copy-Item -LiteralPath $SourceRouter -Destination (Join-Path $InstallDir '.mtls-router.new') -Force
    Move-Item -LiteralPath (Join-Path $InstallDir '.mtls-router.new') -Destination $RouterPath -Force
    if ($env:MTLS_ROUTER_INSTALL_CRASH_POINT -eq 'after-router') { exit 97 }
    Copy-Item -LiteralPath $SourceManager -Destination (Join-Path $InstallDir '.mtls-router-manager.new') -Force
    Move-Item -LiteralPath (Join-Path $InstallDir '.mtls-router-manager.new') -Destination $ManagerPath -Force
    if ($env:MTLS_ROUTER_INSTALL_CRASH_POINT -eq 'after-manager') { exit 97 }
    if (-not (Test-Hash $RouterPath $routerHash) -or -not (Test-Hash $ManagerPath $managerHash)) { Write-Fail '安装后二进制哈希验证失败；下次执行将先回滚。' }
    if ($env:MTLS_ROUTER_INSTALL_CRASH_POINT -eq 'before-receipt') { exit 97 }
    Commit-Pending
}

function Get-AssetUrl($Version, $Asset) {
    if ($DownloadBaseUrl) {
        if ($DownloadBaseUrl -match '/mtls-router-[^/]+$') { return (($DownloadBaseUrl.Substring(0, $DownloadBaseUrl.LastIndexOf('/'))) + "/$Asset") }
        return (($DownloadBaseUrl.TrimEnd('/')) + "/$Asset")
    }
    return "https://github.com/$Repo/releases/download/$Version/$Asset"
}

function Invoke-Download($Url, $OutFile) {
    if (([Uri]$Url).Scheme -ne 'https') { Write-Fail "拒绝非 HTTPS 下载地址：$Url" }
    $parameters = @{ Uri = $Url; OutFile = $OutFile; Headers = @{ 'User-Agent' = 'mtls-router-setup' } }
    if ($DownloadUser -or $DownloadPassword) {
        $secure = ConvertTo-SecureString -String ([string]$DownloadPassword) -AsPlainText -Force
        $parameters.Credential = [PSCredential]::new([string]$DownloadUser, $secure)
    }
    Invoke-WebRequest @parameters
}

function Install-MtlsRouterPair {
    if ($env:MTLS_ROUTER_SKIP_DOWNLOAD -eq '1') { Write-Info '[Download] skipped (MTLS_ROUTER_SKIP_DOWNLOAD=1)'; Repair-PendingInstall; return }
    $assets = Get-PlatformAssets
    $packagedRouter = Join-Path $ScriptDir $assets.Router
    $packagedManager = Join-Path $ScriptDir $assets.Manager
    $manifest = Join-Path $ScriptDir 'SHA256SUMS'
    $anyPayload = @(Get-ChildItem -LiteralPath $ScriptDir -File -Filter 'mtls-router-*-*' -ErrorAction SilentlyContinue).Count -gt 0
    if ($anyPayload) {
        if (-not (Test-Path $packagedRouter -PathType Leaf) -or -not (Test-Path $packagedManager -PathType Leaf) -or -not (Test-Path $manifest -PathType Leaf)) { Write-Fail '检测到不完整的随包二进制对；拒绝联网回退。' }
        Test-Checksum $packagedRouter $manifest $assets.Router
        Test-Checksum $packagedManager $manifest $assets.Manager
        Install-Pair $packagedRouter $packagedManager
        Write-Success "  已验证并安装 mtls-router 与 manager：$InstallDir"
        return
    }
    if (-not $AllowDownload) {
        $interactive = $false
        try { $interactive = -not [Console]::IsInputRedirected -and $null -ne $Host.UI.RawUI } catch {}
        if (-not $interactive) { Write-Fail '未找到随包二进制；非交互安装需显式传入 --download。' }
        if ((Read-Host '未找到随包二进制，是否通过 HTTPS 下载 router 与 manager？[y/N]') -notin @('y', 'Y')) { Write-Fail '已取消联网下载。' }
    }
    $version = if ($env:MTLS_ROUTER_VERSION) { $env:MTLS_ROUTER_VERSION } elseif ($DownloadBaseUrl) { 'latest' } else {
        try { (Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" -Headers @{ 'User-Agent' = 'mtls-router-setup' }).tag_name } catch { Write-Fail '无法获取 GitHub 最新 release。' }
    }
    $routerUrl = Get-AssetUrl $version $assets.Router
    $managerUrl = Get-AssetUrl $version $assets.Manager
    $manifestUrl = Get-AssetUrl $version 'SHA256SUMS'
    if (([Uri]$routerUrl).Scheme -ne 'https' -or ([Uri]$managerUrl).Scheme -ne 'https' -or ([Uri]$manifestUrl).Scheme -ne 'https') { Write-Fail '拒绝非 HTTPS 下载地址。' }
    $tmp = Join-Path ([IO.Path]::GetTempPath()) ('mtls-router-' + [Guid]::NewGuid().ToString('N'))
    New-Item -ItemType Directory -Path $tmp | Out-Null
    try {
        $tmpRouter = Join-Path $tmp $assets.Router; $tmpManager = Join-Path $tmp $assets.Manager; $tmpManifest = Join-Path $tmp 'SHA256SUMS'
        Invoke-Download $routerUrl $tmpRouter
        Invoke-Download $managerUrl $tmpManager
        Invoke-Download $manifestUrl $tmpManifest
        Test-Checksum $tmpRouter $tmpManifest $assets.Router
        Test-Checksum $tmpManager $tmpManifest $assets.Manager
        Install-Pair $tmpRouter $tmpManager
    } finally { Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue }
    Write-Success "  已安装 mtls-router 与 manager：$InstallDir"
}

function Resolve-ManagerPair {
    Repair-PendingInstall
    $assets = Get-PlatformAssets
    $siblingRouter = Join-Path $ScriptDir $assets.Router
    $siblingManager = Join-Path $ScriptDir $assets.Manager
    $manifest = Join-Path $ScriptDir 'SHA256SUMS'
    $anyPayload = @(Get-ChildItem -LiteralPath $ScriptDir -File -Filter 'mtls-router-*-*' -ErrorAction SilentlyContinue).Count -gt 0
    if ($anyPayload) {
        if (-not (Test-Path $siblingRouter -PathType Leaf) -or -not (Test-Path $siblingManager -PathType Leaf) -or -not (Test-Path $manifest -PathType Leaf)) { Write-Fail '随包二进制对不完整；Agent 命令不会联网下载。' }
        Test-Checksum $siblingRouter $manifest $assets.Router
        Test-Checksum $siblingManager $manifest $assets.Manager
        return [pscustomobject]@{ Router = $siblingRouter; Manager = $siblingManager }
    }
    if (-not (Test-Receipt)) { Write-Fail '未找到 receipt 验证的 manager/router；Agent 命令不会隐式下载。请先运行 router install。' }
    return [pscustomobject]@{ Router = $RouterPath; Manager = $ManagerPath }
}

function Invoke-Manager($Request) {
    $pair = Resolve-ManagerPair
    return Invoke-ManagerAt $pair.Manager $pair.Router $Request
}

function Start-MtlsRouter {
    if ($env:MTLS_ROUTER_SKIP_START -eq '1') { Write-Info '[启动] 跳过（MTLS_ROUTER_SKIP_START=1）'; return }
    $result = Invoke-Manager '{"id":"setup","method":"router.start","params":{"owner":"cli"}}'
    Write-Success "  mtls-router 已启动，监听地址通常为 $RouterBaseUrl"
    Write-Info "pid=$($result.result.pid)"
}

function Show-RouterStatus {
    $response = Invoke-Manager '{"id":"status","method":"router.status","params":{}}'
    switch ($response.result.state) {
        { $_ -in @('external_compatible', 'desktop_owned', 'degraded') } { Write-Success 'router running' }
        { $_ -in @('stale', 'unknown_occupant') } { Write-Warn 'router state stale' }
        'absent' { Write-Info 'router 未运行' }
        default { Write-Info "router $($response.result.state)" }
    }
    Write-Info "pid=$($response.result.pid)"
    Write-Info "listen_addr=$($response.result.listen_addr)"
    $state = Join-Path $StateDir 'setup-state.json'
    if (Test-Path $state) {
        $saved = Get-Content $state -Raw | ConvertFrom-Json
        Write-Info "binary_path=$($saved.binary_path)"
        Write-Info "log_path=$($saved.log_path)"
    }
}

function Show-RouterLog($Lines) {
    if ([int]$Lines -lt 1 -or [int]$Lines -gt 1000) { Write-Fail 'router log --tail 需要 1-1000 的行数。' }
    $request = [ordered]@{ id = 'logs'; method = 'router.logs'; params = @{ limit = [int]$Lines } } | ConvertTo-Json -Compress -Depth 5
    (Invoke-Manager $request).result.lines | ForEach-Object { Write-Host $_ }
}

function Stop-Router {
    $pair = Resolve-ManagerPair
    $response = '{"id":"stop","method":"router.stop","params":{}}' | & $pair.Manager serve --router-sidecar $pair.Router
    if ($LASTEXITCODE -ne 0) { Write-Fail 'mtls-router-manager 执行失败。' }
    try { $decoded = @($response)[0] | ConvertFrom-Json } catch { Write-Fail 'manager 返回了无效 JSON。' }
    if ($decoded.error.code -eq 'ROUTER_NOT_FOUND') { Write-Info 'router 未运行'; return }
    if ($decoded.error) { Write-Fail "manager $($decoded.error.code): $($decoded.error.message)" }
    Write-Success 'router stopped'
}

function Get-AgentTargets($Filter, $Detect) {
    if (-not $Filter) { return @($Detect.result.agents | Where-Object detected | ForEach-Object agent) }
    $seen = @{}; $targets = @()
    foreach ($token in @($Filter -split ',' | ForEach-Object Trim | Where-Object { $_ })) {
        if ($token -notin @('claude', 'opencode', 'codex')) { Write-Fail "未知 agent: $token" }
        if ($seen[$token]) { Write-Fail "--agent 列表中有重复项：$token" }
        if (-not @($Detect.result.agents | Where-Object { $_.agent -eq $token -and $_.detected }).Count) { Write-Fail "未检测到 agent: $token" }
        $seen[$token] = $true; $targets += $token
    }
    return $targets
}

function Show-AgentPreview($Filter) {
    $detect = Invoke-Manager '{"id":"detect","method":"agent.detect","params":{}}'
    $targets = @(Get-AgentTargets $Filter $detect)
    if (-not $targets.Count) { Write-Warn '  未检测到 Claude Code、opencode 或 Codex。'; return }
    $request = [ordered]@{ id = 'preview'; method = 'agent.preview'; params = @{ agents = $targets } } | ConvertTo-Json -Compress -Depth 10
    $preview = Invoke-Manager $request
    foreach ($key in $targets) {
        $state = $detect.result.agents | Where-Object agent -eq $key | Select-Object -First 1
        Write-Host "### $($state.name) -> $($state.path)"
        switch ($key) {
            'claude' {
@'
{
  "env": {
    "ANTHROPIC_BASE_URL": "http://127.0.0.1:19099",
    "ANTHROPIC_AUTH_TOKEN": "{UserApiKey}",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-5.5",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-5.5",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.4[1M]",
    "ANTHROPIC_MODEL": "gpt-5.5",
    "ENABLE_TOOL_SEARCH": "true",
    "DISABLE_AUTOUPDATER": "1"
  }
}
'@
            }
            'opencode' {
@'
{
  "provider": {
    "mtls-router": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "mtls-router",
      "options": {"baseURL": "http://127.0.0.1:19099/v1", "apiKey": "{UserApiKey}"},
      "models": {"gpt-5.5": {"name": "GPT-5.5"}, "gpt-5.4": {"name": "GPT-5.4"}}
    }
  }
}
'@
            }
            'codex' {
@'
model_provider = "custom"
model = "gpt-5.5"
disable_response_storage = true

[model_providers.custom]
name = "9router"
wire_api = "responses"
requires_openai_auth = true
base_url = "http://127.0.0.1:19099/v1"
'@
                Write-Host "### Codex -> $($state.auth_path)"
                Write-Host "{`n  `"OPENAI_API_KEY`": `"{UserApiKey}`"`n}"
            }
        }
        Write-Host ''
    }
    if (-not $preview.result.revision_token) { Write-Fail 'manager preview 缺少 revision token。' }
    Write-Info '以上内容由 manager 预览验证；未修改文件。'
}

function Write-AgentConfig($Filter) {
    if (-not $Filter) { Write-Fail '--write-config 需要 --agent=claude,opencode,codex 显式指定要操作的 agent。' }
    $detect = Invoke-Manager '{"id":"detect","method":"agent.detect","params":{}}'
    $targets = @(Get-AgentTargets $Filter $detect)
    $previewRequest = [ordered]@{ id = 'preview'; method = 'agent.preview'; params = @{ agents = $targets } } | ConvertTo-Json -Compress -Depth 10
    $preview = Invoke-Manager $previewRequest
    $apiKey = ''
    try {
        if (-not [Console]::IsInputRedirected) {
            $secure = Read-Host '请输入 mtls-router OPENAI_API_KEY（输入隐藏）' -AsSecureString
            $ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
            try { $apiKey = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr) }
        }
    } catch {}
    if (-not $apiKey) { Write-Fail '写入 Agent 配置需要隐藏交互输入。非交互自动化请向 mtls-router-manager serve 的 agent.write JSON stdin 请求提供 api_key；MTLS_ROUTER_OPENAI_API_KEY 已移除。' }
    $request = [ordered]@{ id = 'write'; method = 'agent.write'; params = @{ agents = $targets; revision_token = $preview.result.revision_token; api_key = $apiKey } } | ConvertTo-Json -Compress -Depth 10
    $response = Invoke-Manager $request
    $apiKey = ''; $request = ''
    foreach ($agent in $response.result.agents) {
        $name = switch ($agent.agent) { 'claude' {'Claude Code'} 'codex' {'Codex'} default {$agent.agent} }
        foreach ($path in $agent.changed) { Write-Success "  已写入 $name 配置: $path" }
        foreach ($backup in $agent.backups) { Write-Info "  已备份: $backup" }
    }
}

function Print-NextSteps {
    Write-Success '============================================================'
    Write-Success '配置完成。'
    Write-Success '============================================================'
    if ($script:RouterStarted) { Write-Info "mtls-router 已在后台运行：`n  $RouterBaseUrl" }
    else { Write-Info "未启动 mtls-router（本次仅处理配置）。如需启动，请运行：`n  $PSCommandPath router start" }
}

function Show-Usage {
@'
用法: setup.ps1 [router|agent] <command> [选项]

默认行为等价于 router setup，不修改 Agent 配置。

  router install|start|setup|status|log [--tail=N]|stop
  agent print-config [--agent=claude,opencode,codex]
  agent write-config --agent=claude,opencode,codex
  --print-config / --write-config   兼容旧别名

安装选项: --download --download-url=URL --download-user=USER
          --download-password=PASSWORD --version=VERSION

Agent 写入仅接受隐藏交互输入。自动化请直接向 mtls-router-manager serve
发送 agent.preview 后再发送含 api_key 的 agent.write 单行 JSON stdin 请求。
'@
}

function Main {
    $action = 'setup'; $agentFilter = ''; $lines = 200
    if ($args.Count) {
        switch ($args[0]) {
            'router' { if ($args.Count -lt 2) { Write-Fail 'router 需要子命令' }; $action = "router-$($args[1])"; $args = @($args | Select-Object -Skip 2) }
            'agent' { if ($args.Count -lt 2) { Write-Fail 'agent 需要子命令' }; $action = switch ($args[1]) { 'print-config' {'print'} 'write-config' {'write'} default { Write-Fail "未知 agent 子命令：$($args[1])" } }; $args = @($args | Select-Object -Skip 2) }
            '--print-config' { $action = 'print'; $args = @($args | Select-Object -Skip 1) }
            '--write-config' { $action = 'write'; $args = @($args | Select-Object -Skip 1) }
            { $_ -in @('-h', '--help') } { Show-Usage; return }
            default { if ($args[0] -notlike '--*') { Write-Fail "未知参数：$($args[0])" } }
        }
    }
    if ($action -notin @('setup','router-install','router-start','router-setup','router-status','router-log','router-stop','print','write')) { Write-Fail "未知 router 子命令：$($action.Substring(7))" }
    $i = 0
    while ($i -lt $args.Count) {
        $a = $args[$i]
        switch -Regex ($a) {
            '^--agent=(.+)$' { $agentFilter = $Matches[1]; $i++; continue }
            '^--agent$' { if (++$i -ge $args.Count) { Write-Fail '--agent 需要参数' }; $agentFilter = $args[$i]; $i++; continue }
            '^--tail=(.+)$' { $lines = $Matches[1]; $i++; continue }
            '^--tail$' { if (++$i -ge $args.Count) { Write-Fail '--tail 需要行数' }; $lines = $args[$i]; $i++; continue }
            '^--download$' { if ($action -notin @('setup','router-install','router-setup')) { Write-Fail '--download 仅适用于 router install/setup' }; $script:AllowDownload = $true; $i++; continue }
            '^--download-url=(.+)$' { $script:DownloadBaseUrl = $Matches[1]; $i++; continue }
            '^--download-url$' { $script:DownloadBaseUrl = $args[++$i]; $i++; continue }
            '^--download-user=(.*)$' { $script:DownloadUser = $Matches[1]; $i++; continue }
            '^--download-user$' { $script:DownloadUser = $args[++$i]; $i++; continue }
            '^--download-password=(.*)$' { $script:DownloadPassword = $Matches[1]; $i++; continue }
            '^--download-password$' { $script:DownloadPassword = $args[++$i]; $i++; continue }
            '^--version=(.+)$' { $env:MTLS_ROUTER_VERSION = $Matches[1]; $i++; continue }
            '^--version$' { $env:MTLS_ROUTER_VERSION = $args[++$i]; $i++; continue }
            '^(-h|--help)$' { Show-Usage; return }
            default { Write-Fail "未知参数：$a" }
        }
    }
    Show-Banner
    switch ($action) {
        { $_ -in @('setup','router-setup') } { Install-MtlsRouterPair; Start-MtlsRouter; if ($env:MTLS_ROUTER_SKIP_START -ne '1') { $script:RouterStarted = $true }; Write-Info '提示：未对 agent 配置做任何改动。' }
        'router-install' { Install-MtlsRouterPair; Print-NextSteps }
        'router-start' { Start-MtlsRouter; if ($env:MTLS_ROUTER_SKIP_START -ne '1') { $script:RouterStarted = $true }; Print-NextSteps }
        'router-status' { Show-RouterStatus }
        'router-log' { Show-RouterLog $lines }
        'router-stop' { Stop-Router }
        'print' { Show-AgentPreview $agentFilter }
        'write' { Write-AgentConfig $agentFilter; Print-NextSteps }
    }
}

Main @args
