#Requires -Version 5.1
<#
.SYNOPSIS
    WSL2 + Claude Code CLI + Unity MCP + Godot MCP Pro + Rust Token Killer 全栈一键部署脚本
.DESCRIPTION
    自动完成以下步骤：
      1. 启用 WSL2 功能 & 安装 Ubuntu 发行版
      2. 检测/安装 Windows 本机 Node.js（AI 客户端 MCP 需要）
      3. WSL2 内安装 Node.js LTS（Claude Code CLI 需要）
      4. 安装 Claude Code CLI (@anthropic-ai/claude-code) + GitHub Copilot CLI
      5. 安装 Unity MCP Server + Godot MCP Pro（Windows + WSL2 双侧）& 写入各 AI 客户端配置
      6. 配置 Windows 防火墙放行 MCP Bridge 端口
      7. 安装 Rust 工具链 & Token Killer (rtk)
      8. 写入 PowerShell Profile 快捷命令
.PARAMETER BridgePort
    Unity MCP Bridge 端口，默认 7890
.PARAMETER InstallDir
    Windows 侧 unity-mcp-server 安装目录
.PARAMETER UnityHubPath
    Unity Hub 路径（写入 MCP 配置 env）
.PARAMETER SkipFirewall
    跳过防火墙规则配置
.PARAMETER SkipWSL
    跳过 WSL2 安装步骤
.NOTES
    首次安装 WSL2 需以管理员身份运行。
    已有 WSL2 可普通终端运行，加 -SkipWSL 跳过 WSL2 安装检查。
#>
param(
    [int]   $BridgePort        = 7890,
    [string]$InstallDir        = "$env:USERPROFILE\unity-mcp-server",
    [string]$UnityHubPath      = "C:\Program Files\Unity Hub\Unity Hub.exe",
    [string]$GodotMcpVersion   = "godot-mcp-pro-v1.14.1",
    [switch]$SkipFirewall,
    [switch]$SkipWSL
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

# ──────────────────────────────────────────────────────────────
#  颜色日志辅助
# ──────────────────────────────────────────────────────────────
function Write-Step { param($msg) Write-Host "`n==== $msg ====" -ForegroundColor Cyan }
function Write-OK   { param($msg) Write-Host "  [OK] $msg"     -ForegroundColor Green }
function Write-Info { param($msg) Write-Host "  [..] $msg"     -ForegroundColor DarkGray }
function Write-Warn { param($msg) Write-Host "  [!!] $msg"     -ForegroundColor Yellow }
function Write-Fail { param($msg) Write-Host "  [ERR] $msg"    -ForegroundColor Red }

# ──────────────────────────────────────────────────────────────
#  加载 .env 配置
# ──────────────────────────────────────────────────────────────
$ScriptDir  = $PSScriptRoot
$EnvFile    = Join-Path $ScriptDir ".env"
$EnvExample = Join-Path $ScriptDir ".env.example"

if (-not (Test-Path $EnvFile)) {
    if (Test-Path $EnvExample) {
        Copy-Item $EnvExample $EnvFile
        Write-Warn "已从 .env.example 创建 .env，请按需编辑后重新运行"
        Write-Warn "  notepad $EnvFile"
        exit 0
    }
}

$Config = @{}
if (Test-Path $EnvFile) {
    Get-Content $EnvFile | Where-Object { $_ -match "^\s*[^#].*=" } | ForEach-Object {
        $parts = $_ -split "=", 2
        $key   = $parts[0].Trim()
        $val   = $parts[1].Trim().Trim('"').Trim("'")
        $Config[$key] = $val
    }
    Write-Info "已加载配置：$EnvFile"
}

$ANTHROPIC_API_KEY  = if ($Config["ANTHROPIC_API_KEY"])  { $Config["ANTHROPIC_API_KEY"]  } else { "" }
$ANTHROPIC_AUTH_TOKEN = if ($Config["ANTHROPIC_AUTH_TOKEN"]) { $Config["ANTHROPIC_AUTH_TOKEN"] } else { "" }
$ANTHROPIC_BASE_URL = if ($Config["ANTHROPIC_BASE_URL"]) { $Config["ANTHROPIC_BASE_URL"] } else { "https://api.lmuai.com" }
$CLAUDE_MODEL       = if ($Config["CLAUDE_MODEL"])       { $Config["CLAUDE_MODEL"]       } else { "claude-sonnet-4-6" }
$WSL_DISTRO         = if ($Config["WSL_DISTRO"])         { $Config["WSL_DISTRO"]         } else { "Ubuntu" }
$SKIP_WSL_INSTALL   = if ($Config["SKIP_WSL_INSTALL"])   { $Config["SKIP_WSL_INSTALL"]   } else { "false" }

# 判断认证方式：灵眸/中转用 AUTH_TOKEN，官方用 API_KEY
$UseLmuAuth = ($ANTHROPIC_AUTH_TOKEN -ne "" -and $ANTHROPIC_BASE_URL -ne "https://api.anthropic.com")

# ──────────────────────────────────────────────────────────────
#  Banner
# ──────────────────────────────────────────────────────────────
Write-Host ""
Write-Host "╔══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║  WSL2 + Claude Code + Unity MCP + Godot MCP Pro + RTK   ║" -ForegroundColor Cyan
 Write-Host "║  AnkleBreaker Unity MCP · Godot MCP Pro · WSL2 Mirror   ║" -ForegroundColor Cyan
Write-Host "╚══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
Write-Host ""

# ══════════════════════════════════════════════════════════════
#  Step 1: WSL2 安装
# ══════════════════════════════════════════════════════════════
Write-Step "1/8  WSL2 环境检测 & 安装"

$distroInstalled = $false
try {
    $dl = wsl --list --quiet 2>&1 | ForEach-Object { $_ -replace "`0","" }
    if ($dl -match [regex]::Escape($WSL_DISTRO)) { $distroInstalled = $true }
} catch {}

if ($distroInstalled) {
    Write-OK "WSL2 + $WSL_DISTRO 已安装，跳过"
} elseif ($SkipWSL -or $SKIP_WSL_INSTALL -eq "true") {
    Write-Warn "跳过 WSL2 安装"
} else {
    $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
               ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    if (-not $isAdmin) {
        Write-Fail "安装 WSL2 需要管理员权限，请以管理员身份运行 PowerShell"
        Write-Info "如已安装 WSL2，请加 -SkipWSL 参数或在 .env 设置 SKIP_WSL_INSTALL=true"
        exit 1
    }
    try {
        $f1 = Get-WindowsOptionalFeature -Online -FeatureName "Microsoft-Windows-Subsystem-Linux" -ErrorAction Stop
        if ($f1.State -ne "Enabled") {
            Enable-WindowsOptionalFeature -Online -FeatureName "Microsoft-Windows-Subsystem-Linux" -NoRestart | Out-Null
        }
        $f2 = Get-WindowsOptionalFeature -Online -FeatureName "VirtualMachinePlatform" -ErrorAction Stop
        if ($f2.State -ne "Enabled") {
            Enable-WindowsOptionalFeature -Online -FeatureName "VirtualMachinePlatform" -NoRestart | Out-Null
        }
    } catch {
        # Fallback: Get-WindowsOptionalFeature 不可用（WMI "没有注册类"），改用 dism.exe
        Write-Warn "Get-WindowsOptionalFeature 不可用，回退至 dism.exe 启用 Windows 功能..."
        $dismOut1 = (dism.exe /online /Get-FeatureInfo /FeatureName:Microsoft-Windows-Subsystem-Linux 2>&1) | Out-String
        if ($dismOut1 -notmatch "State : Enabled") {
            dism.exe /online /Enable-Feature /FeatureName:Microsoft-Windows-Subsystem-Linux /NoRestart | Out-Null
        }
        $dismOut2 = (dism.exe /online /Get-FeatureInfo /FeatureName:VirtualMachinePlatform 2>&1) | Out-String
        if ($dismOut2 -notmatch "State : Enabled") {
            dism.exe /online /Enable-Feature /FeatureName:VirtualMachinePlatform /NoRestart | Out-Null
        }
    }
    wsl --update 2>&1 | Out-Null
    wsl --set-default-version 2 2>&1 | Out-Null
    wsl --install -d $WSL_DISTRO --no-launch 2>&1
    if ($LASTEXITCODE -ne 0) {
        Write-Fail "WSL2 安装失败，系统可能需要重启后重新运行"
        exit 1
    }
    Write-OK "WSL2 $WSL_DISTRO 安装完成"
    Write-Warn "首次安装可能需要重启，重启后重新运行脚本"
}

# ── Step 1b: 首次 OOBE 处理 & 设置 root 为默认用户 ──────────
# Ubuntu OOBE 检测：若无 UID≥1000 的普通用户则弹交互提示
# 解决方案：创建占位用户满足检查 + wsl.conf 设 default=root
Write-Info "初始化 WSL2（跳过 OOBE，设置 root 为默认用户）..."
$initCmd = @'
# 创建占位用户，满足 Ubuntu OOBE "至少有一个真实用户" 的检查
id wsluser &>/dev/null || useradd -m -s /bin/bash -u 1000 wsluser

# 覆盖写入 /etc/wsl.conf（单一 [user] 块，避免重复冲突）
printf '[boot]\nsystemd=false\n\n[user]\ndefault=root\n' > /etc/wsl.conf

# 禁用 Ubuntu OOBE 自动运行脚本（如存在）
rm -f /etc/profile.d/01-wsl-oobe.sh 2>/dev/null || true
rm -f /usr/lib/ubuntu-advantage/ua-messaging.service 2>/dev/null || true
true
'@
# 用 --user root 初始化 rootfs（本身也会跳过 OOBE 交互）
wsl -d $WSL_DISTRO --user root -- bash -c "exit 0" 2>$null
Start-Sleep 2
# 写入配置
$b64Init = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($initCmd))
wsl -d $WSL_DISTRO --user root -- bash -c "echo '$b64Init' | base64 -d | bash" 2>$null
Write-OK "/etc/wsl.conf 已写入 (default=root)，占位用户已创建"

# 重启使 wsl.conf 生效
wsl --terminate $WSL_DISTRO 2>$null
Start-Sleep 2
Write-OK "WSL2 已重启，后续命令将以 root 运行"

# ── WSL2 执行辅助函数 ─────────────────────────────────────────
function Invoke-WSL {
    param([string]$Command, [switch]$IgnoreError)

    if ($Command -match "`n") {
        # 多行命令：base64 编码后在 bash 内解码执行，完全规避 PowerShell 管道 CRLF 问题
        $CleanCmd = $Command -replace "`r`n","`n" -replace "`r","`n"
        $b64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($CleanCmd))
        $result = wsl -d $WSL_DISTRO --user root -- bash -c "echo '$b64' | base64 -d | bash" 2>&1 |
                  ForEach-Object { ($_ -replace "`0","").ToString() }
    } else {
        $result = wsl -d $WSL_DISTRO --user root -- bash -c $Command 2>&1 |
                  ForEach-Object { ($_ -replace "`0","").ToString() }
    }

    if ($LASTEXITCODE -ne 0 -and -not $IgnoreError) {
        Write-Fail "WSL 命令失败 (exit $LASTEXITCODE)"
        Write-Fail ($result -join "`n")
        exit 1
    }
    return ($result -join "`n")
}

# 用 exit code 方式测试连通性，避免输出编码干扰
wsl -d $WSL_DISTRO --user root -- bash -c "exit 0" 2>$null
if ($LASTEXITCODE -ne 0) {
    Write-Fail "无法访问 WSL2 发行版 '$WSL_DISTRO'"
    exit 1
}
Write-OK "WSL2 ($WSL_DISTRO) 连接正常 (root)"

# ── Step 1a: 配置 .wslconfig（镜像网络模式仅 Windows 11 22H2+ 支持） ────
$wslCfgPath = "$env:USERPROFILE\.wslconfig"
$osBuild = [System.Environment]::OSVersion.Version.Build
$supportsmirror = ($osBuild -ge 22621)   # Win11 22H2+
$needRestart = $false
if ($supportsmirror) {
    $wslCfgContent = @"
[wsl2]
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true
"@
    if (Test-Path $wslCfgPath) {
        $existing = Get-Content $wslCfgPath -Raw
        if ($existing -notmatch "networkingMode=mirrored") {
            Set-Content $wslCfgPath $wslCfgContent -Encoding UTF8
            $needRestart = $true
            Write-OK ".wslconfig 已更新 -> networkingMode=mirrored"
        }
    } else {
        Set-Content $wslCfgPath $wslCfgContent -Encoding UTF8
        $needRestart = $true
        Write-OK ".wslconfig 已创建 -> networkingMode=mirrored"
    }
    if ($needRestart) {
        Write-Info "重启 WSL2 以应用镜像网络模式..."
        wsl --shutdown 2>$null
        Start-Sleep 3
    }
} else {
    Write-Warn "当前 Windows 版本（Build $osBuild）不支持 WSL2 镜像网络模式（需要 Windows 11 22H2+ / Build 22621+），已跳过 .wslconfig 镜像配置"
    Write-Info "WSL2 将使用默认 NAT 网络模式，代理请在 WSL2 内手动配置"
}

# ══════════════════════════════════════════════════════════════
#  Step 2: Windows 本机 Node.js 检查（AI 客户端 MCP 需要）
# ══════════════════════════════════════════════════════════════
Write-Step "2/8  Windows Node.js 检查"

$winNode = Get-Command node -ErrorAction SilentlyContinue
if ($winNode) {
    $winNodeVer = & node --version
    Write-OK "Windows Node.js 已安装: $winNodeVer"
} else {
    Write-Warn "Windows 本机未检测到 Node.js"
    Write-Info "尝试通过 winget 安装 Node.js LTS..."
    $wingetCmd = Get-Command winget -ErrorAction SilentlyContinue
    if ($wingetCmd) {
        winget install --id OpenJS.NodeJS.LTS --silent --accept-source-agreements --accept-package-agreements 2>&1 | Out-Null
        # 刷新 PATH
        $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" +
                    [System.Environment]::GetEnvironmentVariable("Path","User")
        $winNode = Get-Command node -ErrorAction SilentlyContinue
        if ($winNode) {
            Write-OK "Node.js 安装成功: $(& node --version)"
        } else {
            Write-Warn "winget 安装后未找到 node，请手动安装 Node.js 18+："
            Write-Warn "  https://nodejs.org/zh-cn/download"
            Write-Warn "  或: winget install OpenJS.NodeJS.LTS"
        }
    } else {
        Write-Warn "未找到 winget，请手动安装 Node.js 18+："
        Write-Warn "  https://nodejs.org/zh-cn/download"
    }
}

# ══════════════════════════════════════════════════════════════
#  Step 3: WSL2 系统依赖 & Node.js
# ══════════════════════════════════════════════════════════════
Write-Step "3/8  WSL2 Node.js LTS"

# 检测 WSL2 原生 node（排除通过 WSL interop 调用的 Windows node，路径含 /mnt/）
$nodeVer = Invoke-WSL @'
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
_n=$(which node 2>/dev/null)
if [ -n "$_n" ] && ! echo "$_n" | grep -q '/mnt/'; then
  node --version
else
  echo MISSING
fi
true
'@ -IgnoreError
if ($nodeVer -match "v\d+") {
    Write-OK "WSL2 Node.js 已安装 (原生): $($nodeVer.Trim())"
} else {
    Write-Info "安装 WSL2 Node.js LTS (via nvm)..."
    $installNodeCmd = @'
export DEBIAN_FRONTEND=noninteractive
sudo apt-get install -y -qq curl ca-certificates wslu 2>/dev/null
# 安装 nvm
export NVM_DIR="$HOME/.nvm"
if [ ! -d "$NVM_DIR" ]; then
  curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
fi
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# 安装 Node.js LTS
nvm install --lts
nvm use --lts
# 写入 .bashrc（幂等）
grep -q 'NVM_DIR' ~/.bashrc || cat >> ~/.bashrc << 'NVMEOF'

# nvm (Node Version Manager)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
NVMEOF
node --version
npm --version
true
'@
    Invoke-WSL $installNodeCmd
    Write-OK "WSL2 Node.js 安装完成"
}

# ══════════════════════════════════════════════════════════════
#  Step 4: Claude Code CLI + GitHub Copilot CLI (WSL2)
# ══════════════════════════════════════════════════════════════
Write-Step "4/8  Claude Code CLI + GitHub Copilot CLI"

# 检测 WSL2 原生 claude（排除通过 WSL interop 调用的 Windows claude.exe，路径含 /mnt/）
$claudeVer = Invoke-WSL @'
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
_c=$(which claude 2>/dev/null)
if [ -n "$_c" ] && ! echo "$_c" | grep -q '/mnt/'; then
  claude --version 2>/dev/null
else
  echo MISSING
fi
true
'@ -IgnoreError
if ($claudeVer -notmatch "MISSING") {
    Write-OK "Claude Code 已安装 (WSL2 原生): $($claudeVer.Trim())"
} else {
    Write-Info "安装 @anthropic-ai/claude-code (WSL2 原生)..."
    $installClaudeCmd = @'
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
npm install -g @anthropic-ai/claude-code --quiet
which claude
claude --version
true
'@
    Invoke-WSL $installClaudeCmd
    Write-OK "Claude Code 安装完成"
}

# 检测 WSL2 原生 GitHub CLI（排除通过 WSL interop 调用的 Windows gh.exe，路径含 /mnt/）
$ghVer = Invoke-WSL @'
_g=$(which gh 2>/dev/null)
if [ -n "$_g" ] && ! echo "$_g" | grep -q '/mnt/'; then
    gh --version 2>/dev/null | head -1
else
    echo MISSING
fi
true
'@ -IgnoreError

$ghCopilotInstalled = Invoke-WSL @'
_g=$(which gh 2>/dev/null)
if [ -n "$_g" ] && ! echo "$_g" | grep -q '/mnt/'; then
    gh extension list 2>/dev/null | grep -q 'github/gh-copilot'
    echo $?
else
    echo 1
fi
true
'@ -IgnoreError

if ($ghVer -notmatch "MISSING") {
        Write-OK "GitHub CLI 已安装 (WSL2 原生): $($ghVer.Trim())"
} else {
        Write-Info "安装 GitHub CLI (WSL2 原生)..."
        $installGhCmd = @'
export DEBIAN_FRONTEND=noninteractive
sudo mkdir -p -m 755 /etc/apt/keyrings
if [ ! -f /etc/apt/keyrings/githubcli-archive-keyring.gpg ]; then
    curl -fsSL --connect-timeout 20 --max-time 30 https://cli.github.com/packages/githubcli-archive-keyring.gpg \
        | sudo dd of=/etc/apt/keyrings/githubcli-archive-keyring.gpg status=none
    sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
fi
if [ ! -f /etc/apt/sources.list.d/github-cli.list ]; then
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
        | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
fi
if sudo apt-get update -qq -o Acquire::http::Timeout=20 -o Acquire::https::Timeout=20 2>/dev/null && \
   sudo apt-get install -y -qq -o Acquire::http::Timeout=30 gh 2>/dev/null; then
    echo GH_INSTALL_OK
else
    echo GH_INSTALL_FAIL
fi
true
'@
        $ghInstallResult = Invoke-WSL $installGhCmd -IgnoreError
        $ghVer = Invoke-WSL "gh --version 2>/dev/null | head -1 || echo MISSING" -IgnoreError
        if ($ghInstallResult -match "GH_INSTALL_FAIL" -or $ghVer -match "MISSING") {
            Write-Warn "GitHub CLI 安装失败（网络超时），跳过；后续可手动安装：wsl -d Ubuntu -- sudo apt install gh"
            $ghVer = "MISSING"
        } else {
            Write-OK "GitHub CLI 安装完成: $($ghVer.Trim())"
        }
}

if ($ghCopilotInstalled.Trim() -eq "0") {
        Write-OK "GitHub Copilot CLI 扩展已安装 (WSL2 原生)"
} else {
        Write-Info "安装 GitHub Copilot CLI 扩展 (gh-copilot)..."
        $installGhCopilotCmd = @'
if gh extension list 2>/dev/null | grep -q 'github/gh-copilot'; then
    gh extension upgrade github/gh-copilot 2>/dev/null || true
else
    gh extension install github/gh-copilot
fi
gh extension list | grep 'github/gh-copilot' || true
true
'@
        Invoke-WSL $installGhCopilotCmd
        Write-OK "GitHub Copilot CLI 扩展已安装（命令入口：gh copilot）"
}

# 写入 Claude 配置（WSL2 侧）— 含 allowedTools 完整白名单（自动信任，无需每次确认）
$mcpAllowedTools = @(
    # Claude 内置工具
    "Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","TodoRead","TodoWrite",
    # Editor State
    "mcp__unity-mcp__unity_editor_ping","mcp__unity-mcp__unity_editor_state","mcp__unity-mcp__unity_project_info",
    # Scene
    "mcp__unity-mcp__unity_scene_info","mcp__unity-mcp__unity_scene_open","mcp__unity-mcp__unity_scene_save",
    "mcp__unity-mcp__unity_scene_new","mcp__unity-mcp__unity_scene_hierarchy","mcp__unity-mcp__unity_scene_stats",
    # GameObject
    "mcp__unity-mcp__unity_gameobject_create","mcp__unity-mcp__unity_gameobject_delete",
    "mcp__unity-mcp__unity_gameobject_info","mcp__unity-mcp__unity_gameobject_set_transform",
    "mcp__unity-mcp__unity_gameobject_duplicate","mcp__unity-mcp__unity_gameobject_set_active",
    "mcp__unity-mcp__unity_gameobject_reparent",
    # Component
    "mcp__unity-mcp__unity_component_add","mcp__unity-mcp__unity_component_remove",
    "mcp__unity-mcp__unity_component_get_properties","mcp__unity-mcp__unity_component_set_property",
    "mcp__unity-mcp__unity_component_set_reference","mcp__unity-mcp__unity_component_batch_wire",
    "mcp__unity-mcp__unity_component_get_referenceable",
    # Asset
    "mcp__unity-mcp__unity_asset_list","mcp__unity-mcp__unity_asset_import",
    "mcp__unity-mcp__unity_asset_delete","mcp__unity-mcp__unity_asset_create_prefab",
    "mcp__unity-mcp__unity_asset_instantiate_prefab",
    # Script & Code
    "mcp__unity-mcp__unity_script_create","mcp__unity-mcp__unity_script_read",
    "mcp__unity-mcp__unity_script_update","mcp__unity-mcp__unity_execute_code",
    # Material
    "mcp__unity-mcp__unity_material_create","mcp__unity-mcp__unity_renderer_set_material",
    # Build & Play Mode
    "mcp__unity-mcp__unity_build","mcp__unity-mcp__unity_play_mode",
    # Console & Compilation
    "mcp__unity-mcp__unity_console_log","mcp__unity-mcp__unity_console_clear",
    "mcp__unity-mcp__unity_get_compilation_errors",
    # Editor Actions
    "mcp__unity-mcp__unity_execute_menu_item","mcp__unity-mcp__unity_undo",
    "mcp__unity-mcp__unity_redo","mcp__unity-mcp__unity_undo_history",
    # Selection & Search
    "mcp__unity-mcp__unity_selection_get","mcp__unity-mcp__unity_selection_set",
    "mcp__unity-mcp__unity_selection_focus_scene_view","mcp__unity-mcp__unity_selection_find_by_type",
    "mcp__unity-mcp__unity_search_by_component","mcp__unity-mcp__unity_search_by_tag",
    "mcp__unity-mcp__unity_search_by_layer","mcp__unity-mcp__unity_search_by_name",
    "mcp__unity-mcp__unity_search_assets","mcp__unity-mcp__unity_search_missing_references",
    # Screenshots & Graphics
    "mcp__unity-mcp__unity_screenshot_game","mcp__unity-mcp__unity_screenshot_scene",
    "mcp__unity-mcp__unity_graphics_scene_capture","mcp__unity-mcp__unity_graphics_game_capture",
    # Prefab
    "mcp__unity-mcp__unity_prefab_info","mcp__unity-mcp__unity_set_object_reference",
    # Packages
    "mcp__unity-mcp__unity_packages_list","mcp__unity-mcp__unity_packages_add",
    "mcp__unity-mcp__unity_packages_remove","mcp__unity-mcp__unity_packages_search",
    "mcp__unity-mcp__unity_packages_info",
    # Queue & Multi-Agent
    "mcp__unity-mcp__unity_queue_info","mcp__unity-mcp__unity_agents_list","mcp__unity-mcp__unity_agent_log",
    # Advanced Tools proxy（200+ 工具通过此代理访问）
    "mcp__unity-mcp__unity_list_advanced_tools","mcp__unity-mcp__unity_advanced_tool",
    # Unity Hub
    "mcp__unity-mcp__unity_hub_list_editors","mcp__unity-mcp__unity_hub_available_releases",
    "mcp__unity-mcp__unity_hub_install_editor","mcp__unity-mcp__unity_hub_install_modules",
    "mcp__unity-mcp__unity_hub_get_install_path","mcp__unity-mcp__unity_hub_set_install_path",
    # Multi-Instance & Project Context
    "mcp__unity-mcp__unity_list_instances","mcp__unity-mcp__unity_select_instance",
    "mcp__unity-mcp__unity_get_project_context",
    # ─── Godot MCP Pro ───────────────────────────────────────
    # Project & Filesystem
    "mcp__godot-mcp-pro__get_project_info","mcp__godot-mcp-pro__get_filesystem_tree",
    "mcp__godot-mcp-pro__search_files","mcp__godot-mcp-pro__search_in_files",
    "mcp__godot-mcp-pro__get_project_settings","mcp__godot-mcp-pro__get_project_statistics",
    "mcp__godot-mcp-pro__uid_to_project_path","mcp__godot-mcp-pro__project_path_to_uid",
    # Scene
    "mcp__godot-mcp-pro__get_scene_tree","mcp__godot-mcp-pro__get_scene_file_content",
    "mcp__godot-mcp-pro__get_scene_exports","mcp__godot-mcp-pro__get_scene_dependencies",
    "mcp__godot-mcp-pro__create_scene","mcp__godot-mcp-pro__open_scene",
    "mcp__godot-mcp-pro__save_scene","mcp__godot-mcp-pro__add_scene_instance",
    # Node
    "mcp__godot-mcp-pro__get_node_properties","mcp__godot-mcp-pro__get_node_groups",
    "mcp__godot-mcp-pro__get_signals","mcp__godot-mcp-pro__find_nodes_in_group",
    "mcp__godot-mcp-pro__find_nodes_by_type","mcp__godot-mcp-pro__find_signal_connections",
    "mcp__godot-mcp-pro__find_node_references",
    "mcp__godot-mcp-pro__add_node","mcp__godot-mcp-pro__duplicate_node",
    "mcp__godot-mcp-pro__move_node","mcp__godot-mcp-pro__rename_node",
    "mcp__godot-mcp-pro__update_property","mcp__godot-mcp-pro__add_resource",
    "mcp__godot-mcp-pro__set_anchor_preset",
    "mcp__godot-mcp-pro__connect_signal","mcp__godot-mcp-pro__disconnect_signal",
    "mcp__godot-mcp-pro__set_node_groups",
    "mcp__godot-mcp-pro__batch_set_property","mcp__godot-mcp-pro__cross_scene_set_property",
    # Script
    "mcp__godot-mcp-pro__list_scripts","mcp__godot-mcp-pro__read_script",
    "mcp__godot-mcp-pro__get_open_scripts","mcp__godot-mcp-pro__validate_script",
    "mcp__godot-mcp-pro__create_script","mcp__godot-mcp-pro__edit_script",
    "mcp__godot-mcp-pro__attach_script",
    # Editor & Output
    "mcp__godot-mcp-pro__get_editor_errors","mcp__godot-mcp-pro__get_output_log",
    "mcp__godot-mcp-pro__get_editor_screenshot","mcp__godot-mcp-pro__get_editor_performance",
    "mcp__godot-mcp-pro__clear_output","mcp__godot-mcp-pro__reload_plugin",
    "mcp__godot-mcp-pro__reload_project",
    # Resource & Shader
    "mcp__godot-mcp-pro__read_resource","mcp__godot-mcp-pro__get_resource_preview",
    "mcp__godot-mcp-pro__create_resource","mcp__godot-mcp-pro__edit_resource",
    "mcp__godot-mcp-pro__read_shader","mcp__godot-mcp-pro__get_shader_params",
    "mcp__godot-mcp-pro__create_shader","mcp__godot-mcp-pro__edit_shader",
    "mcp__godot-mcp-pro__assign_shader_material","mcp__godot-mcp-pro__set_shader_param",
    # Animation
    "mcp__godot-mcp-pro__list_animations","mcp__godot-mcp-pro__get_animation_info",
    "mcp__godot-mcp-pro__create_animation","mcp__godot-mcp-pro__add_animation_track",
    "mcp__godot-mcp-pro__set_animation_keyframe",
    "mcp__godot-mcp-pro__get_animation_tree_structure",
    "mcp__godot-mcp-pro__create_animation_tree","mcp__godot-mcp-pro__add_state_machine_state",
    "mcp__godot-mcp-pro__add_state_machine_transition",
    "mcp__godot-mcp-pro__set_blend_tree_node","mcp__godot-mcp-pro__set_tree_parameter",
    # Audio
    "mcp__godot-mcp-pro__get_audio_bus_layout","mcp__godot-mcp-pro__get_audio_info",
    "mcp__godot-mcp-pro__add_audio_bus","mcp__godot-mcp-pro__set_audio_bus",
    "mcp__godot-mcp-pro__add_audio_bus_effect","mcp__godot-mcp-pro__add_audio_player",
    # Input
    "mcp__godot-mcp-pro__get_input_actions","mcp__godot-mcp-pro__set_input_action",
    # 3D & Physics
    "mcp__godot-mcp-pro__get_physics_layers","mcp__godot-mcp-pro__get_collision_info",
    "mcp__godot-mcp-pro__set_physics_layers",
    "mcp__godot-mcp-pro__add_mesh_instance","mcp__godot-mcp-pro__setup_lighting",
    "mcp__godot-mcp-pro__set_material_3d","mcp__godot-mcp-pro__setup_environment",
    "mcp__godot-mcp-pro__setup_camera_3d","mcp__godot-mcp-pro__add_gridmap",
    "mcp__godot-mcp-pro__setup_collision","mcp__godot-mcp-pro__add_raycast",
    "mcp__godot-mcp-pro__setup_physics_body",
    # Navigation
    "mcp__godot-mcp-pro__get_navigation_info",
    "mcp__godot-mcp-pro__setup_navigation_region","mcp__godot-mcp-pro__bake_navigation_mesh",
    "mcp__godot-mcp-pro__setup_navigation_agent","mcp__godot-mcp-pro__set_navigation_layers",
    # Particles
    "mcp__godot-mcp-pro__get_particle_info",
    "mcp__godot-mcp-pro__create_particles","mcp__godot-mcp-pro__set_particle_material",
    "mcp__godot-mcp-pro__set_particle_color_gradient","mcp__godot-mcp-pro__apply_particle_preset",
    # Theme
    "mcp__godot-mcp-pro__get_theme_info",
    "mcp__godot-mcp-pro__create_theme","mcp__godot-mcp-pro__set_theme_color",
    "mcp__godot-mcp-pro__set_theme_constant","mcp__godot-mcp-pro__set_theme_font_size",
    "mcp__godot-mcp-pro__set_theme_stylebox",
    # Tilemap
    "mcp__godot-mcp-pro__tilemap_get_cell","mcp__godot-mcp-pro__tilemap_get_info",
    "mcp__godot-mcp-pro__tilemap_get_used_cells",
    "mcp__godot-mcp-pro__tilemap_set_cell","mcp__godot-mcp-pro__tilemap_fill_rect",
    # Autoload & Project Settings
    "mcp__godot-mcp-pro__get_autoload","mcp__godot-mcp-pro__add_autoload",
    "mcp__godot-mcp-pro__set_project_setting",
    # Analysis
    "mcp__godot-mcp-pro__find_unused_resources","mcp__godot-mcp-pro__analyze_signal_flow",
    "mcp__godot-mcp-pro__analyze_scene_complexity","mcp__godot-mcp-pro__find_script_references",
    "mcp__godot-mcp-pro__detect_circular_dependencies",
    "mcp__godot-mcp-pro__get_performance_monitors",
    # Export
    "mcp__godot-mcp-pro__get_export_info","mcp__godot-mcp-pro__list_export_presets",
    # Game Runtime (need play_scene first)
    "mcp__godot-mcp-pro__play_scene","mcp__godot-mcp-pro__stop_scene",
    "mcp__godot-mcp-pro__get_game_scene_tree","mcp__godot-mcp-pro__get_game_node_properties",
    "mcp__godot-mcp-pro__get_game_screenshot","mcp__godot-mcp-pro__set_game_node_property",
    "mcp__godot-mcp-pro__find_nodes_by_script",
    # Input Simulation
    "mcp__godot-mcp-pro__simulate_key","mcp__godot-mcp-pro__simulate_mouse_click",
    "mcp__godot-mcp-pro__simulate_mouse_move","mcp__godot-mcp-pro__simulate_action",
    "mcp__godot-mcp-pro__simulate_sequence",
    # Capture & Recording
    "mcp__godot-mcp-pro__capture_frames","mcp__godot-mcp-pro__record_frames",
    "mcp__godot-mcp-pro__monitor_properties","mcp__godot-mcp-pro__batch_get_properties",
    "mcp__godot-mcp-pro__start_recording","mcp__godot-mcp-pro__stop_recording",
    "mcp__godot-mcp-pro__replay_recording",
    # UI & Navigation (Runtime)
    "mcp__godot-mcp-pro__find_ui_elements","mcp__godot-mcp-pro__click_button_by_text",
    "mcp__godot-mcp-pro__wait_for_node","mcp__godot-mcp-pro__find_nearby_nodes",
    "mcp__godot-mcp-pro__navigate_to","mcp__godot-mcp-pro__move_to",
    # Testing
    "mcp__godot-mcp-pro__run_test_scenario","mcp__godot-mcp-pro__assert_node_state",
    "mcp__godot-mcp-pro__assert_screen_text","mcp__godot-mcp-pro__run_stress_test",
    "mcp__godot-mcp-pro__get_test_report","mcp__godot-mcp-pro__compare_screenshots"
)
$claudeSettingsJson = if ($UseLmuAuth) {
    # 灵眸 / 中转 API：使用 env.ANTHROPIC_AUTH_TOKEN（避免与 ANTHROPIC_API_KEY 冲突）
    [ordered]@{
        env = [ordered]@{
            ANTHROPIC_BASE_URL                        = $ANTHROPIC_BASE_URL
            ANTHROPIC_AUTH_TOKEN                      = $ANTHROPIC_AUTH_TOKEN
            API_TIMEOUT_MS                            = "3000000"
            CLAUDE_CODE_ATTRIBUTION_HEADER            = "0"
            CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC  = "1"
        }
        model        = $CLAUDE_MODEL
        allowedTools = $mcpAllowedTools
    } | ConvertTo-Json -Depth 4
} else {
    # Anthropic 官方：使用 model + allowedTools，API Key 由环境变量注入
    [ordered]@{ model = $CLAUDE_MODEL; allowedTools = $mcpAllowedTools } | ConvertTo-Json -Depth 3
}
$writeClaudeSettingsCmd = @"
mkdir -p ~/.claude
cat > ~/.claude/settings.json << 'SETTINGS'
$claudeSettingsJson
SETTINGS
true
"@
Invoke-WSL $writeClaudeSettingsCmd -IgnoreError | Out-Null

# 写入 bash 环境变量（WSL2 侧）
# 灵眸模式：auth token 已在 settings.json env 块中，清理旧 bashrc 变量避免冲突
# 官方模式：写入 ANTHROPIC_API_KEY 到 bashrc
$cleanOldVarsCmd = @'
sed -i '/ANTHROPIC_API_KEY/d' ~/.bashrc ~/.profile 2>/dev/null || true
sed -i '/ANTHROPIC_BASE_URL/d' ~/.bashrc ~/.profile 2>/dev/null || true
sed -i '/CLAUDE_MODEL/d'       ~/.bashrc ~/.profile 2>/dev/null || true
sed -i '/# Claude Code CLI/d'  ~/.bashrc ~/.profile 2>/dev/null || true
true
'@
Invoke-WSL $cleanOldVarsCmd -IgnoreError | Out-Null

if (-not $UseLmuAuth -and $ANTHROPIC_API_KEY) {
    $profBlock  = "export ANTHROPIC_API_KEY='$ANTHROPIC_API_KEY'\n"
    $profBlock += "export ANTHROPIC_BASE_URL='$ANTHROPIC_BASE_URL'\n"
    $profBlock += "export CLAUDE_MODEL='$CLAUDE_MODEL'\n"
    $addEnvCmd = @"
printf '\n# Claude Code CLI\n$profBlock' >> ~/.bashrc
true
"@
    Invoke-WSL $addEnvCmd -IgnoreError | Out-Null
}

# Windows 侧 Claude 配置
$claudeDir = "$env:USERPROFILE\.claude"
if (-not (Test-Path $claudeDir)) { New-Item -ItemType Directory -Path $claudeDir | Out-Null }
$claudeSettingsJson | Set-Content "$claudeDir\settings.json" -Encoding UTF8
if ($UseLmuAuth) {
    # 灵眸：token 已在 settings.json 中，清除可能冲突的 Windows 用户级变量
    [System.Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY",  $null, "User")
    [System.Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL", $null, "User")
    [System.Environment]::SetEnvironmentVariable("CLAUDE_MODEL",       $null, "User")
} elseif ($ANTHROPIC_API_KEY) {
    [System.Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY",  $ANTHROPIC_API_KEY,  "User")
    [System.Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL", $ANTHROPIC_BASE_URL, "User")
    [System.Environment]::SetEnvironmentVariable("CLAUDE_MODEL",       $CLAUDE_MODEL,       "User")
}
Write-OK "Claude Code 配置已写入（Windows + WSL2）"

# ══════════════════════════════════════════════════════════════
#  Step 5: Unity MCP Server 安装
# ══════════════════════════════════════════════════════════════
Write-Step "5/8  MCP Servers (Unity + Godot MCP Pro)"
Write-Info "MCP Server:  https://github.com/AnkleBreaker-Studio/unity-mcp-server"
Write-Info "Unity Plugin: https://github.com/AnkleBreaker-Studio/unity-mcp-plugin"

# ── 5a. Windows 侧安装（供 Claude Desktop / Cursor / Windsurf / VS Code）────
Write-Info "安装 Windows 侧 MCP Server -> $InstallDir"
if (Test-Path "$InstallDir\.git") {
    Write-Info "已存在，执行 git pull..."
    Push-Location $InstallDir
    try { git pull --quiet 2>&1 | Out-Null } finally { Pop-Location }
} else {
    if (Test-Path $InstallDir) { Remove-Item $InstallDir -Recurse -Force }
    git clone --quiet --depth 1 https://github.com/AnkleBreaker-Studio/unity-mcp-server.git $InstallDir
}

$winNodeExists = (Get-Command node -ErrorAction SilentlyContinue) -ne $null
if ($winNodeExists) {
    Push-Location $InstallDir
    try { & npm install --prefer-offline --quiet 2>&1 | Out-Null } finally { Pop-Location }
    Write-OK "Windows MCP Server 就绪: $InstallDir"
} else {
    Write-Warn "Windows Node.js 未就绪，跳过 Windows 侧 npm install（请安装 Node.js 后重新运行）"
}

# ── 5b. WSL2 侧安装（供 Claude Code CLI）─────────────────────
Write-Info "安装 WSL2 侧 MCP Server..."
$wslMcpDir = "~/.mcp-servers/unity-mcp-server"
$wslMcpCmd = @'
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
mkdir -p $HOME/.mcp-servers
if [ -d $HOME/.mcp-servers/unity-mcp-server/.git ]; then
  git -C $HOME/.mcp-servers/unity-mcp-server pull --quiet 2>/dev/null || true
else
  git clone --quiet --depth 1 https://github.com/AnkleBreaker-Studio/unity-mcp-server.git \
      $HOME/.mcp-servers/unity-mcp-server 2>/dev/null \
  || git -c http.proxy="" -c https.proxy="" clone --quiet --depth 1 \
      https://github.com/AnkleBreaker-Studio/unity-mcp-server.git \
      $HOME/.mcp-servers/unity-mcp-server
fi
if [ -d $HOME/.mcp-servers/unity-mcp-server ]; then
  cd $HOME/.mcp-servers/unity-mcp-server
  # 修复可能由 root 遗留的权限问题
  sudo chown -R $(whoami):$(whoami) . 2>/dev/null || true
  npm install --prefer-offline --quiet 2>/dev/null || npm install --quiet
fi
echo MCP_WSL_OK
true
'@
Invoke-WSL $wslMcpCmd

$wslHome   = (Invoke-WSL "echo `$HOME" -IgnoreError).Trim()
$wslScript = "$wslHome/.mcp-servers/unity-mcp-server/src/index.js"
Write-OK "WSL2 MCP Server 就绪: $wslScript"

# ── 5c. 写入各 AI 客户端 MCP 配置 ───────────────────────────
$winScript = ($InstallDir -replace "\\","/") + "/src/index.js"

$mcpEntry = @{
    command = "node"
    args    = @($winScript)
    env     = @{
        UNITY_HUB_PATH    = $UnityHubPath
        UNITY_BRIDGE_PORT = "$BridgePort"
    }
}

$wslMcpEntry = @{
    command = "wsl"
    args    = @("-d", $WSL_DISTRO, "--", "node", $wslScript)
    env     = @{
        UNITY_BRIDGE_PORT = "$BridgePort"
    }
}

function Merge-McpConfig {
    param([string]$ConfigFile, [hashtable]$Entry, [string]$Label, [string]$ServerName = "unity-mcp")
    $dir = Split-Path $ConfigFile
    if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
    # PS5.1 兼容：手动将 PSCustomObject 转为普通 Hashtable
    function ConvertTo-Hashtable($obj) {
        if ($obj -is [System.Management.Automation.PSCustomObject]) {
            $h = @{}
            foreach ($p in $obj.PSObject.Properties) { $h[$p.Name] = ConvertTo-Hashtable $p.Value }
            return $h
        } elseif ($obj -is [System.Collections.IEnumerable] -and $obj -isnot [string]) {
            return @($obj | ForEach-Object { ConvertTo-Hashtable $_ })
        }
        return $obj
    }
    $cfg = @{ mcpServers = @{} }
    if (Test-Path $ConfigFile) {
        try {
            $raw = Get-Content $ConfigFile -Raw | ConvertFrom-Json
            $converted = ConvertTo-Hashtable $raw
            if ($converted -is [hashtable]) { $cfg = $converted }
        } catch {}
    }
    if (-not $cfg.ContainsKey("mcpServers")) { $cfg["mcpServers"] = @{} }
    $cfg["mcpServers"][$ServerName] = $Entry
    $cfg | ConvertTo-Json -Depth 10 | Set-Content $ConfigFile -Encoding UTF8
    Write-OK "$Label -> $ConfigFile"
}

# Claude Desktop（Windows）
Merge-McpConfig "$env:APPDATA\Claude\claude_desktop_config.json"       $mcpEntry "Claude Desktop"
# Cursor
Merge-McpConfig "$env:USERPROFILE\.cursor\mcp.json"                    $mcpEntry "Cursor"
# Windsurf（两种路径）
$windsurfDir = if (Test-Path "$env:APPDATA\Windsurf") { "$env:APPDATA\Windsurf" } else { "$env:USERPROFILE\.codeium\windsurf" }
Merge-McpConfig "$windsurfDir\mcp_config.json"                         $mcpEntry "Windsurf"
# VS Code
Merge-McpConfig "$env:APPDATA\Code\User\mcp.json"                      $mcpEntry "VS Code"
# Claude Code CLI（WSL2）——用 `claude mcp add --scope user` 写入全局用户级 MCP 配置
$wslMcpCfgCmd = @"
export NVM_DIR="`$HOME/.nvm"
[ -s "`$NVM_DIR/nvm.sh" ] && . "`$NVM_DIR/nvm.sh"
# 先移除旧条目（幂等），再添加
claude mcp remove unity-mcp --scope user 2>/dev/null || true
claude mcp add --scope user unity-mcp node $wslScript -e UNITY_BRIDGE_PORT=$BridgePort
echo "Claude Code MCP configured"
true
"@
Invoke-WSL $wslMcpCfgCmd -IgnoreError | Out-Null
Write-OK "Claude Code CLI (WSL2) MCP -> unity-mcp (node $wslScript)"

# ══════════════════════════════════════════════════════════════#  Step 5d: Godot MCP Pro 安装（本地 ZIP 包，无需联网）
# ════════════════════════════════════════════════════════════
Write-Step "5d  Godot MCP Pro"
Write-Info "版本目录: $GodotMcpVersion"
Write-Info "源目录: $ScriptDir\$GodotMcpVersion"

$GodotMcpSrcDir = Join-Path $ScriptDir $GodotMcpVersion
$GodotMcpSrcSrv = Join-Path $GodotMcpSrcDir "server"
$GodotMcpWinDir = Join-Path $env:USERPROFILE "godot-mcp-pro"

# ── 5d-i. Windows 侧：robocopy 同步 server/，排除 node_modules ─────────────
if (Test-Path $GodotMcpSrcSrv) {
    Write-Info "同步 server/ -> $GodotMcpWinDir"
    # robocopy 退出码 0-7 均为正常，需屏蔽错误级
    $rcArgs = @($GodotMcpSrcSrv, $GodotMcpWinDir, "/E", "/XD", "node_modules",
                "/NFL", "/NDL", "/NJH", "/NJS", "/nc", "/ns", "/np")
    & robocopy @rcArgs 2>&1 | Out-Null
    if ($winNodeExists) {
        Push-Location $GodotMcpWinDir
        try { & node build/setup.js install 2>&1 | Out-Null } finally { Pop-Location }
    }
    $godotWinEntry = @{
        command = "node"
        args    = @(($GodotMcpWinDir -replace "\\", "/") + "/build/index.js")
    }
    Merge-McpConfig "$env:APPDATA\Claude\claude_desktop_config.json"  $godotWinEntry "Claude Desktop (Godot MCP Pro)" "godot-mcp-pro"
    Merge-McpConfig "$env:USERPROFILE\.cursor\mcp.json"               $godotWinEntry "Cursor (Godot MCP Pro)"         "godot-mcp-pro"
    $windsurfDir2 = if (Test-Path "$env:APPDATA\Windsurf") { "$env:APPDATA\Windsurf" } else { "$env:USERPROFILE\.codeium\windsurf" }
    Merge-McpConfig "$windsurfDir2\mcp_config.json"                   $godotWinEntry "Windsurf (Godot MCP Pro)"       "godot-mcp-pro"
    Merge-McpConfig "$env:APPDATA\Code\User\mcp.json"                 $godotWinEntry "VS Code (Godot MCP Pro)"        "godot-mcp-pro"
    Write-OK "Godot MCP Pro (Windows) 就绪: $GodotMcpWinDir"
} else {
    $GodotMcpWinDir = ""
    Write-Warn "未找到 $GodotMcpVersion\server\，跳过 Godot MCP Pro Windows 侧安装"
    Write-Warn "请确认 $GodotMcpVersion/ 目录与 deploy.ps1 位于同一目录"
}

# ── 5d-ii. WSL2 侧：复制 server/ 到 ~/.mcp-servers/godot-mcp-pro/ ────────────
Write-Info "安装 Godot MCP Pro (WSL2)..."
$scriptDirFwd    = $ScriptDir -replace "\\", "/"
$godotWslSrcBase = (Invoke-WSL "wslpath '$scriptDirFwd/$GodotMcpVersion/server'" -IgnoreError).Trim()
$godotWslDest    = "$wslHome/.mcp-servers/godot-mcp-pro"
$godotWslScript  = "$godotWslDest/build/index.js"

$installGodotWslCmd = @"
export NVM_DIR="`$HOME/.nvm"
[ -s "`$NVM_DIR/nvm.sh" ] && . "`$NVM_DIR/nvm.sh"
GODOT_SRC="$godotWslSrcBase"
GODOT_DST="$godotWslDest"
if [ ! -d "`$GODOT_SRC" ]; then echo "GODOT_SRC_MISSING: `$GODOT_SRC"; exit 0; fi
mkdir -p "`$GODOT_DST"
if command -v rsync >/dev/null 2>&1; then
    rsync -a --delete --exclude='node_modules' "`$GODOT_SRC/" "`$GODOT_DST/"
else
    find "`$GODOT_SRC" -mindepth 1 -maxdepth 1 ! -name 'node_modules' -exec cp -r {} "`$GODOT_DST/" \;
fi
cd "`$GODOT_DST"
node build/setup.js install
echo GODOT_WSL_OK
true
"@

$godotWslResult = Invoke-WSL $installGodotWslCmd -IgnoreError
if ($godotWslResult -match "GODOT_WSL_OK") {
    Write-OK "Godot MCP Pro (WSL2) 就绪: $godotWslScript"
    $godotWslMcpCmd = @"
export NVM_DIR="`$HOME/.nvm"
[ -s "`$NVM_DIR/nvm.sh" ] && . "`$NVM_DIR/nvm.sh"
claude mcp remove godot-mcp-pro --scope user 2>/dev/null || true
claude mcp add --scope user godot-mcp-pro node $godotWslScript
echo "Godot MCP Pro Claude Code configured"
true
"@
    Invoke-WSL $godotWslMcpCmd -IgnoreError | Out-Null
    Write-OK "Claude Code CLI (WSL2) MCP -> godot-mcp-pro (node $godotWslScript)"
} elseif ($godotWslResult -match "GODOT_SRC_MISSING") {
    Write-Warn "WSL2 无法访问源目录，跳过 WSL2 侧 Godot MCP Pro 安装"
    $godotWslScript = ""
} else {
    Write-Warn "Godot MCP Pro WSL2 安装异常，跳过"
    $godotWslScript = ""
}

# ════════════════════════════════════════════════════════════#  Step 6: Windows 防火墙放行 MCP Bridge 端口
# ══════════════════════════════════════════════════════════════
Write-Step "6/8  Windows 防火墙规则"

if ($SkipFirewall) {
    Write-Warn "已跳过防火墙配置（-SkipFirewall）"
} else {
    $portRange  = "$BridgePort-$($BridgePort + 9)"
    $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
               ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    if ($isAdmin) {
        foreach ($dir in @("Inbound","Outbound")) {
            $name = "Unity MCP Bridge $dir"
            Get-NetFirewallRule -DisplayName $name -ErrorAction SilentlyContinue |
                Remove-NetFirewallRule -ErrorAction SilentlyContinue
            New-NetFirewallRule -DisplayName $name -Direction $dir `
                -LocalPort $portRange -Protocol TCP -Action Allow -Profile Any | Out-Null
            Write-OK "防火墙规则: $name (TCP $portRange)"
        }
    } else {
        Write-Warn "非管理员权限，使用提升权限添加防火墙规则..."
        $cmd = @"
`$p = '$portRange'
foreach (`$d in @('Inbound','Outbound')) {
    `$n = "Unity MCP Bridge `$d"
    Get-NetFirewallRule -DisplayName `$n -ErrorAction SilentlyContinue | Remove-NetFirewallRule -ErrorAction SilentlyContinue
    New-NetFirewallRule -DisplayName `$n -Direction `$d -LocalPort `$p -Protocol TCP -Action Allow -Profile Any | Out-Null
    Write-Host "OK: `$n"
}
Start-Sleep 1
"@
        $enc  = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd))
        $proc = Start-Process powershell.exe -ArgumentList "-NoProfile -EncodedCommand $enc" `
                    -Verb RunAs -Wait -PassThru
        if ($proc.ExitCode -eq 0) {
            Write-OK "防火墙规则已添加 (TCP $portRange)"
        } else {
            Write-Warn "防火墙配置可能未成功，请手动放行 TCP $portRange"
        }
    }
}

# ══════════════════════════════════════════════════════════════
#  Step 7: Rust 工具链 & Token Killer (WSL2)
# ══════════════════════════════════════════════════════════════
Write-Step "7/8  Rust & Token Killer"

$rustVer = Invoke-WSL ". ~/.cargo/env 2>/dev/null; rustc --version 2>/dev/null || echo MISSING" -IgnoreError
if ($rustVer -notmatch "MISSING") {
    Write-OK "Rust 已安装: $($rustVer.Trim())"
} else {
    Write-Info "安装 Rust 工具链..."
    $installRustCmd = @"
sudo apt-get install -y -qq build-essential pkg-config libssl-dev 2>/dev/null || true
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup-init.sh
sh /tmp/rustup-init.sh -y --default-toolchain stable --no-modify-path
source ~/.cargo/env
rustc --version
"@
    Invoke-WSL $installRustCmd
    Invoke-WSL "grep -q 'cargo/env' ~/.bashrc  || echo 'source ~/.cargo/env 2>/dev/null || true' >> ~/.bashrc"  -IgnoreError | Out-Null
    Invoke-WSL "grep -q 'cargo/env' ~/.profile || echo 'source ~/.cargo/env 2>/dev/null || true' >> ~/.profile" -IgnoreError | Out-Null
    Write-OK "Rust 安装完成"
}

$rtkVer = Invoke-WSL ". ~/.cargo/env 2>/dev/null; rtk --version 2>/dev/null || echo MISSING" -IgnoreError
if ($rtkVer -notmatch "MISSING") {
    Write-OK "rtk 已安装: $($rtkVer.Trim())"
} else {
    Write-Info "安装 rtk (Rust Token Killer)..."
    $installRtkCmd = @"
. ~/.cargo/env 2>/dev/null || true
CARGO_NET_GIT_FETCH_WITH_CLI=true cargo install --git https://github.com/rtk-ai/rtk 2>&1 | tail -5
"@
    Invoke-WSL $installRtkCmd -IgnoreError
    $rtkCheck = Invoke-WSL ". ~/.cargo/env 2>/dev/null; rtk --version 2>/dev/null || echo FAILED" -IgnoreError
    if ($rtkCheck -match "FAILED") {
        Write-Warn "rtk 安装失败，可手动运行: cargo install --git https://github.com/rtk-ai/rtk"
    } else {
        Write-OK "rtk 安装成功: $($rtkCheck.Trim())"
    }
}

# rtk init -g：安装 Claude Code PreToolUse hook（幂等，yes 管道自动确认所有提示）
Invoke-WSL ". ~/.cargo/env 2>/dev/null; yes | rtk init -g --auto-patch 2>/dev/null || true" -IgnoreError | Out-Null

# 将 settings.json 中的 hook command 改为绝对路径（避免 Claude Code 找不到 rtk）
$fixRtkHookCmd = @'
. ~/.cargo/env 2>/dev/null || true
RTK_BIN=$(which rtk 2>/dev/null)
if [ -n "$RTK_BIN" ] && [ -f ~/.claude/settings.json ]; then
  python3 -c "
import json, sys
p='/root/.claude/settings.json'
with open(p) as f: d=json.load(f)
hooks=d.get('hooks',{})
changed=False
for event in hooks:
    for h in (hooks[event] if isinstance(hooks[event],list) else [hooks[event]]):
        if isinstance(h,dict) and 'command' in h and 'rtk hook' in h['command']:
            h['command']=h['command'].replace('rtk hook','$RTK_BIN hook')
            changed=True
if changed:
    with open(p,'w') as f: json.dump(d,f,indent=2)
    print('hook path updated to $RTK_BIN')
" 2>/dev/null || true
fi
true
'@
Invoke-WSL $fixRtkHookCmd -IgnoreError | Out-Null
Write-OK "rtk hook 已注册（绝对路径），重启 Claude Code 后生效"

# ══════════════════════════════════════════════════════════════
#  Step 8: PowerShell Profile 配置
# ══════════════════════════════════════════════════════════════
Write-Step "8/8  PowerShell Profile"

$pwshProfile = "$env:USERPROFILE\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
$profileDir  = Split-Path $pwshProfile
if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir | Out-Null }

$profileBlock = @"

# ── Claude Dev Stack (generated by deploy.ps1) ────────────────
`$env:ANTHROPIC_BASE_URL = "$ANTHROPIC_BASE_URL"
$(if ($ANTHROPIC_API_KEY) { "`$env:ANTHROPIC_API_KEY = `"$ANTHROPIC_API_KEY`"" } else { "# ANTHROPIC_API_KEY= (配置 .env 后重新运行)" })
`$env:CLAUDE_MODEL = "$CLAUDE_MODEL"
function claude-wsl { wsl -d $WSL_DISTRO -- bash -ic 'claude' }
function gh-copilot-wsl { wsl -d $WSL_DISTRO -- gh copilot @args }
function unity-mcp-status { Invoke-RestMethod http://127.0.0.1:$BridgePort/api/ping -ErrorAction SilentlyContinue }
# ─────────────────────────────────────────────────────────────
"@

$existing = if (Test-Path $pwshProfile) { Get-Content $pwshProfile -Raw } else { "" }
if ($existing -match "Claude Dev Stack") {
    $existing = $existing -replace "(?ms)# ── Claude Dev Stack.*?# ─{60}", $profileBlock
    Set-Content $pwshProfile $existing -Encoding UTF8
} else {
    Add-Content $pwshProfile $profileBlock -Encoding UTF8
}

$policy = Get-ExecutionPolicy -Scope CurrentUser
if ($policy -in @("Restricted","Undefined")) {
    Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force
}
Write-OK "PowerShell Profile 已配置: $pwshProfile"

# ══════════════════════════════════════════════════════════════
#  安装总结
# ══════════════════════════════════════════════════════════════
Write-Host ""
Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Green
Write-Host "║                    ✅  全栈部署完成                          ║" -ForegroundColor Green
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Green

$items = @(
    @("WSL2 内核",     (Invoke-WSL "uname -r 2>/dev/null" -IgnoreError).Trim()),
    @("Node.js (Win)", (& node --version 2>$null)),
    @("Node.js (WSL)", $(if ($nodeVer -match "v\d+") { $nodeVer.Trim() } else { "" })),
    @("Claude Code",   $(if ($claudeVer -notmatch "MISSING") { $claudeVer.Trim() } else { "" })),
    @("GitHub CLI",    $(if ($ghVer -notmatch "MISSING") { $ghVer.Trim() } else { "" })),
    @("Copilot CLI",   $(if ($ghCopilotInstalled.Trim() -eq "0") { "gh extension github/gh-copilot" } else { "" })),
    @("Rust",          (Invoke-WSL ". ~/.cargo/env && rustc --version 2>/dev/null" -IgnoreError).Trim()),
    @("rtk",           (Invoke-WSL ". ~/.cargo/env && rtk --version 2>/dev/null" -IgnoreError).Trim()),
    @("Unity MCP",     $InstallDir),
    @("Godot MCP Pro", $(if ($GodotMcpWinDir) { $GodotMcpWinDir } else { "(未安装)" })),
    @("Bridge Port",   "TCP $BridgePort  (防火墙已放行 $BridgePort-$($BridgePort+9))")
)
foreach ($it in $items) {
    Write-Host ("  {0,-16} {1}" -f $it[0], $(if ($it[1]) { $it[1] } else { "(未安装)" })) -ForegroundColor White
}

Write-Host ""
Write-Host "  ━━━━ 后续步骤 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
Write-Host "  1) 在 Unity 项目中安装 MCP Plugin（每个项目一次）：" -ForegroundColor White
Write-Host "     Window > Package Manager > + > Add from git URL:" -ForegroundColor DarkGray
Write-Host "     https://github.com/AnkleBreaker-Studio/unity-mcp-plugin.git" -ForegroundColor Yellow
Write-Host ""
Write-Host "  2) 打开 Unity 后确认 Bridge 在线（浏览器验证）：" -ForegroundColor White
Write-Host "     http://127.0.0.1:$BridgePort/api/ping" -ForegroundColor Yellow
Write-Host ""
Write-Host "  3) 重启 AI 客户端（Claude Desktop / Cursor / Windsurf）" -ForegroundColor White
Write-Host "     MCP 配置已自动写入各客户端配置文件" -ForegroundColor DarkGray
Write-Host ""
Write-Host "  4) 在 Claude Code (WSL2) 中使用：" -ForegroundColor White
Write-Host "     claude-wsl  →  claude  →  /mcp" -ForegroundColor DarkGray
Write-Host ""
Write-Host "  5) 在 WSL2 中使用 GitHub Copilot CLI：" -ForegroundColor White
Write-Host "     先执行 gh auth login，然后使用 gh-copilot-wsl suggest \"your prompt\"" -ForegroundColor DarkGray
Write-Host ""
Write-Host "  6) 在 Godot 项目中启用 MCP Pro 插件（每个项目一次）：" -ForegroundColor White
Write-Host "     复制 addons/godot_mcp/ 到 Godot 项目根目录" -ForegroundColor DarkGray
Write-Host "     在 Project Settings → Plugins 中启用 Godot MCP Pro" -ForegroundColor DarkGray
Write-Host "     插件源目录: $GodotMcpSrcDir\addons\godot_mcp\" -ForegroundColor Yellow
Write-Host "     启用后 Godot 将在 6505-6509 端口监听 MCP 连接" -ForegroundColor DarkGray
Write-Host ""
if (-not $ANTHROPIC_API_KEY -and -not $ANTHROPIC_AUTH_TOKEN) {
    Write-Warn "  ⚠ 未设置 API Key，请编辑 .env 后重新运行"
    Write-Warn "    灵眸用户：填写 ANTHROPIC_AUTH_TOKEN=sk-xxx"
    Write-Warn "    官方用户：填写 ANTHROPIC_API_KEY=sk-xxx"
}
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Green
