#Requires -Version 5.1
<#
.SYNOPSIS
    Windows 原生 Claude Code CLI + GitHub Copilot CLI + MCP + Rust Token Killer 一键部署脚本
.DESCRIPTION
    不依赖 WSL2，全部组件原生运行在 Windows 上。完成以下步骤：
      1. Node.js LTS 检测 / 安装（winget）
      2. Claude Code CLI (@anthropic-ai/claude-code) 安装
      3. GitHub CLI + Copilot CLI 扩展安装（winget + gh extension）
      4. Unity MCP Server 安装 & 写入各 AI 客户端 MCP 配置
      5. Godot MCP Pro 安装（本地包）& 写入各 AI 客户端 MCP 配置
      6. Rust 工具链 + RTK (Rust Token Killer) 安装
      7. RTK hook 注册到 Claude Code + Copilot Instructions 生成
      8. PowerShell Profile 快捷命令写入
.PARAMETER BridgePort
    Unity MCP Bridge 端口，默认 7890
.PARAMETER SkipFirewall
    跳过防火墙规则配置
.NOTES
    防火墙规则需要管理员权限，其余步骤普通用户即可运行。
    脚本可重复运行（幂等），已安装的组件自动跳过。
#>
param(
    [int]   $BridgePort     = 7890,
    [string]$GodotMcpVersion = "godot-mcp-pro-v1.14.1",
    [switch]$SkipFirewall
)

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
    }
}

$Cfg = @{}
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("'")
        $Cfg[$key] = $val
    }
    Write-Info "已加载配置：$EnvFile"
}

$ANTHROPIC_API_KEY    = $Cfg["ANTHROPIC_API_KEY"]    ?? ""
$ANTHROPIC_AUTH_TOKEN = $Cfg["ANTHROPIC_AUTH_TOKEN"] ?? ""
$ANTHROPIC_BASE_URL   = $Cfg["ANTHROPIC_BASE_URL"]   ?? "https://api.lmuai.com"
$CLAUDE_MODEL         = $Cfg["CLAUDE_MODEL"]         ?? "claude-sonnet-4-6"
$BRIDGE_PORT_CFG      = $Cfg["BRIDGE_PORT"]          ?? "$BridgePort"
$UnityHubPath         = $Cfg["UNITY_HUB_PATH"]       ?? "C:\Program Files\Unity Hub\Unity Hub.exe"
$UnityMcpInstallDir   = $Cfg["UNITY_MCP_INSTALL_DIR"] ?? "$env:USERPROFILE\unity-mcp-server"
$GodotMcpPackageDir   = $Cfg["GODOT_MCP_PACKAGE_DIR"] ?? "."

# 参数 BridgePort 优先于 .env
if ($BridgePort -eq 7890 -and $BRIDGE_PORT_CFG -ne "7890") { $BridgePort = [int]$BRIDGE_PORT_CFG }

# 判断认证方式
$UseLmuAuth = ($ANTHROPIC_AUTH_TOKEN -ne "" -and $ANTHROPIC_BASE_URL -ne "https://api.anthropic.com")

# ──────────────────────────────────────────────────────────────
#  Banner
# ──────────────────────────────────────────────────────────────
Write-Host ""
Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║  Windows Native: Claude Code + Copilot CLI + MCP + RTK      ║" -ForegroundColor Cyan
Write-Host "║  Unity MCP · Godot MCP Pro · Rust Token Killer              ║" -ForegroundColor Cyan
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
Write-Host ""

# ──────────────────────────────────────────────────────────────
#  辅助：刷新当前进程 PATH
# ──────────────────────────────────────────────────────────────
function Refresh-Path {
    $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
                [System.Environment]::GetEnvironmentVariable("Path", "User")
}

# ──────────────────────────────────────────────────────────────
#  辅助：winget 安装（静默）
# ──────────────────────────────────────────────────────────────
function Install-Winget {
    param([string]$Id, [string]$DisplayName)
    $wg = Get-Command winget -ErrorAction SilentlyContinue
    if (-not $wg) {
        Write-Warn "未找到 winget，请手动安装 $DisplayName"
        return $false
    }
    Write-Info "winget install $Id ..."
    winget install --id $Id --silent --accept-source-agreements --accept-package-agreements 2>&1 | Out-Null
    Refresh-Path
    return $true
}

# ──────────────────────────────────────────────────────────────
#  辅助：写入 / 合并 MCP 配置文件（JSON）
# ──────────────────────────────────────────────────────────────
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 }
    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"
}

# ══════════════════════════════════════════════════════════════
#  Step 1: Node.js LTS
# ══════════════════════════════════════════════════════════════
Write-Step "1/8  Node.js LTS"

$nodeCmd = Get-Command node -ErrorAction SilentlyContinue
if ($nodeCmd) {
    Write-OK "Node.js 已安装: $(& node --version)"
} else {
    Write-Info "通过 winget 安装 Node.js LTS..."
    $ok = Install-Winget "OpenJS.NodeJS.LTS" "Node.js LTS"
    $nodeCmd = Get-Command node -ErrorAction SilentlyContinue
    if ($nodeCmd) {
        Write-OK "Node.js 安装完成: $(& node --version)"
    } else {
        Write-Warn "未能自动安装 Node.js，请手动安装后重新运行："
        Write-Warn "  https://nodejs.org/zh-cn/download  或  winget install OpenJS.NodeJS.LTS"
    }
}

# ══════════════════════════════════════════════════════════════
#  Step 2: Claude Code CLI
# ══════════════════════════════════════════════════════════════
Write-Step "2/8  Claude Code CLI"

$claudeCmd = Get-Command claude -ErrorAction SilentlyContinue
if ($claudeCmd) {
    Write-OK "Claude Code 已安装: $(& claude --version 2>&1 | Select-Object -First 1)"
} else {
    if (Get-Command node -ErrorAction SilentlyContinue) {
        Write-Info "安装 @anthropic-ai/claude-code ..."
        & npm install -g @anthropic-ai/claude-code --quiet 2>&1 | Out-Null
        Refresh-Path
        $claudeCmd = Get-Command claude -ErrorAction SilentlyContinue
        if ($claudeCmd) {
            Write-OK "Claude Code 安装完成: $(& claude --version 2>&1 | Select-Object -First 1)"
        } else {
            Write-Warn "安装后未找到 claude 命令，请重开终端后重试"
        }
    } else {
        Write-Warn "Node.js 未就绪，跳过 Claude Code 安装"
    }
}

# ── 写入 Claude Code 配置（settings.json）──────────────────
$mcpAllowedTools = @(
    "Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","TodoRead","TodoWrite",
    # Unity MCP
    "mcp__unity-mcp__unity_editor_ping","mcp__unity-mcp__unity_editor_state","mcp__unity-mcp__unity_project_info",
    "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",
    "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",
    "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",
    "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",
    "mcp__unity-mcp__unity_script_create","mcp__unity-mcp__unity_script_read",
    "mcp__unity-mcp__unity_script_update","mcp__unity-mcp__unity_execute_code",
    "mcp__unity-mcp__unity_material_create","mcp__unity-mcp__unity_renderer_set_material",
    "mcp__unity-mcp__unity_build","mcp__unity-mcp__unity_play_mode",
    "mcp__unity-mcp__unity_console_log","mcp__unity-mcp__unity_console_clear",
    "mcp__unity-mcp__unity_get_compilation_errors",
    "mcp__unity-mcp__unity_execute_menu_item","mcp__unity-mcp__unity_undo",
    "mcp__unity-mcp__unity_redo","mcp__unity-mcp__unity_undo_history",
    "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",
    "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",
    "mcp__unity-mcp__unity_prefab_info","mcp__unity-mcp__unity_set_object_reference",
    "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",
    "mcp__unity-mcp__unity_queue_info","mcp__unity-mcp__unity_agents_list","mcp__unity-mcp__unity_agent_log",
    "mcp__unity-mcp__unity_list_advanced_tools","mcp__unity-mcp__unity_advanced_tool",
    "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",
    "mcp__unity-mcp__unity_list_instances","mcp__unity-mcp__unity_select_instance",
    "mcp__unity-mcp__unity_get_project_context",
    # Godot MCP Pro
    "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",
    "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",
    "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",
    "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",
    "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",
    "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",
    "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",
    "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",
    "mcp__godot-mcp-pro__get_input_actions","mcp__godot-mcp-pro__set_input_action",
    "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",
    "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",
    "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",
    "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",
    "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",
    "mcp__godot-mcp-pro__get_autoload","mcp__godot-mcp-pro__add_autoload",
    "mcp__godot-mcp-pro__set_project_setting",
    "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",
    "mcp__godot-mcp-pro__get_export_info","mcp__godot-mcp-pro__list_export_presets",
    "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",
    "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",
    "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",
    "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",
    "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) {
    [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 {
    [ordered]@{ model = $CLAUDE_MODEL; allowedTools = $mcpAllowedTools } | ConvertTo-Json -Depth 3
}

$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) {
    [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 配置已写入 $claudeDir\settings.json"

# ══════════════════════════════════════════════════════════════
#  Step 3: GitHub CLI + Copilot CLI 扩展
# ══════════════════════════════════════════════════════════════
Write-Step "3/8  GitHub CLI + Copilot CLI"

$ghCmd = Get-Command gh -ErrorAction SilentlyContinue
if ($ghCmd) {
    Write-OK "GitHub CLI 已安装: $(& gh --version 2>&1 | Select-Object -First 1)"
} else {
    Write-Info "通过 winget 安装 GitHub CLI..."
    $ok = Install-Winget "GitHub.cli" "GitHub CLI"
    $ghCmd = Get-Command gh -ErrorAction SilentlyContinue
    if ($ghCmd) {
        Write-OK "GitHub CLI 安装完成: $(& gh --version 2>&1 | Select-Object -First 1)"
    } else {
        Write-Warn "GitHub CLI 安装后未找到，请重开终端或手动安装：winget install GitHub.cli"
    }
}

if ($ghCmd) {
    $extList = & gh extension list 2>&1 | Out-String
    if ($extList -match "github/gh-copilot") {
        Write-OK "GitHub Copilot CLI 扩展已安装"
    } else {
        Write-Info "安装 GitHub Copilot CLI 扩展..."
        try {
            & gh extension install github/gh-copilot 2>&1 | Out-Null
            Write-OK "GitHub Copilot CLI 扩展安装完成（入口：gh copilot）"
        } catch {
            Write-Warn "安装失败（可能需要先 gh auth login），请手动运行：gh extension install github/gh-copilot"
        }
    }
}

# ══════════════════════════════════════════════════════════════
#  Step 4: Unity MCP Server
# ══════════════════════════════════════════════════════════════
Write-Step "4/8  Unity MCP Server"
Write-Info "安装目录: $UnityMcpInstallDir"

$gitCmd = Get-Command git -ErrorAction SilentlyContinue
if (-not $gitCmd) {
    Write-Warn "未找到 git，跳过 Unity MCP Server 安装"
    Write-Warn "请安装 Git for Windows：winget install Git.Git"
    $UnityMcpReady = $false
} else {
    if (Test-Path "$UnityMcpInstallDir\.git") {
        Write-Info "已存在，更新至最新..."
        Push-Location $UnityMcpInstallDir
        try { & git pull --quiet 2>&1 | Out-Null } finally { Pop-Location }
    } else {
        if (Test-Path $UnityMcpInstallDir) { Remove-Item $UnityMcpInstallDir -Recurse -Force }
        & git clone --quiet --depth 1 https://github.com/AnkleBreaker-Studio/unity-mcp-server.git $UnityMcpInstallDir
    }

    if (Get-Command node -ErrorAction SilentlyContinue) {
        Push-Location $UnityMcpInstallDir
        try { & npm install --prefer-offline --quiet 2>&1 | Out-Null } finally { Pop-Location }
        Write-OK "Unity MCP Server 就绪"
        $UnityMcpReady = $true
    } else {
        Write-Warn "Node.js 未就绪，跳过 npm install"
        $UnityMcpReady = $false
    }
}

# ── 确定入口文件 ──────────────────────────────────────────────
$unityEntry = ""
if ($UnityMcpReady) {
    foreach ($f in @("src/index.js","build/index.js","index.js")) {
        $fp = Join-Path $UnityMcpInstallDir $f
        if (Test-Path $fp) { $unityEntry = $fp -replace "\\","/"; break }
    }
}

# ── 写入 MCP 配置（四大 AI 客户端 + Claude Code CLI）─────────
if ($unityEntry) {
    $unityMcpEntry = @{
        command = "node"
        args    = @($unityEntry)
        env     = @{ UNITY_HUB_PATH = $UnityHubPath; UNITY_BRIDGE_PORT = "$BridgePort" }
    }
    Merge-McpConfig "$env:APPDATA\Claude\claude_desktop_config.json"  $unityMcpEntry "Claude Desktop"
    Merge-McpConfig "$env:USERPROFILE\.cursor\mcp.json"               $unityMcpEntry "Cursor"
    $wsDirC = if (Test-Path "$env:APPDATA\Windsurf") { "$env:APPDATA\Windsurf" } else { "$env:USERPROFILE\.codeium\windsurf" }
    Merge-McpConfig "$wsDirC\mcp_config.json"                         $unityMcpEntry "Windsurf"
    Merge-McpConfig "$env:APPDATA\Code\User\mcp.json"                 $unityMcpEntry "VS Code"
    # Claude Code CLI（Windows）
    if (Get-Command claude -ErrorAction SilentlyContinue) {
        & claude mcp remove unity-mcp --scope user 2>&1 | Out-Null
        & claude mcp add --scope user unity-mcp node $unityEntry -e "UNITY_BRIDGE_PORT=$BridgePort" 2>&1 | Out-Null
        Write-OK "Claude Code CLI MCP -> unity-mcp"
    }
}

# ══════════════════════════════════════════════════════════════
#  Step 5: Godot MCP Pro
# ══════════════════════════════════════════════════════════════
Write-Step "5/8  Godot MCP Pro"

# 解析 Godot MCP Pro 本地包路径（相对于本脚本目录）
$godotPkgBase = if ([System.IO.Path]::IsPathRooted($GodotMcpPackageDir)) {
    $GodotMcpPackageDir
} else {
    [System.IO.Path]::GetFullPath((Join-Path $ScriptDir $GodotMcpPackageDir))
}
$godotSrcDir  = Join-Path $godotPkgBase "$GodotMcpVersion\server"
$godotWinDir  = "$env:USERPROFILE\godot-mcp-pro"

if (-not (Test-Path $godotSrcDir)) {
    Write-Warn "未找到 Godot MCP Pro 源目录: $godotSrcDir"
    Write-Warn "请确认 .env 中 GODOT_MCP_PACKAGE_DIR 指向包含 $GodotMcpVersion\ 的目录"
    $godotEntry = ""
} else {
    Write-Info "同步 server/ -> $godotWinDir"
    $rcArgs = @($godotSrcDir, $godotWinDir, "/E", "/XD", "node_modules",
                "/NFL", "/NDL", "/NJH", "/NJS", "/nc", "/ns", "/np")
    & robocopy @rcArgs 2>&1 | Out-Null

    if (Get-Command node -ErrorAction SilentlyContinue) {
        Push-Location $godotWinDir
        try { & node build/setup.js install 2>&1 | Out-Null } finally { Pop-Location }
        Write-OK "Godot MCP Pro 就绪: $godotWinDir"
    } else {
        Write-Warn "Node.js 未就绪，跳过 Godot MCP Pro setup"
    }

    $godotEntry = ($godotWinDir -replace "\\","/") + "/build/index.js"
    $godotMcpEntry = @{ command = "node"; args = @($godotEntry) }

    Merge-McpConfig "$env:APPDATA\Claude\claude_desktop_config.json"  $godotMcpEntry "Claude Desktop (Godot)" "godot-mcp-pro"
    Merge-McpConfig "$env:USERPROFILE\.cursor\mcp.json"               $godotMcpEntry "Cursor (Godot)"         "godot-mcp-pro"
    $wsDirG = if (Test-Path "$env:APPDATA\Windsurf") { "$env:APPDATA\Windsurf" } else { "$env:USERPROFILE\.codeium\windsurf" }
    Merge-McpConfig "$wsDirG\mcp_config.json"                         $godotMcpEntry "Windsurf (Godot)"       "godot-mcp-pro"
    Merge-McpConfig "$env:APPDATA\Code\User\mcp.json"                 $godotMcpEntry "VS Code (Godot)"        "godot-mcp-pro"
    if (Get-Command claude -ErrorAction SilentlyContinue) {
        & claude mcp remove godot-mcp-pro --scope user 2>&1 | Out-Null
        & claude mcp add --scope user godot-mcp-pro node $godotEntry 2>&1 | Out-Null
        Write-OK "Claude Code CLI MCP -> godot-mcp-pro"
    }
}

# ══════════════════════════════════════════════════════════════
#  Step 6: 防火墙放行 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 "非管理员权限，尝试以提升权限添加防火墙规则..."
        $fwCmd = @"
`$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($fwCmd))
        $proc = Start-Process powershell.exe -ArgumentList "-NoProfile -EncodedCommand $enc" `
                    -Verb RunAs -Wait -PassThru -ErrorAction SilentlyContinue
        if ($proc -and $proc.ExitCode -eq 0) {
            Write-OK "防火墙规则已添加 (TCP $portRange)"
        } else {
            Write-Warn "防火墙配置未成功，请手动以管理员运行：New-NetFirewallRule ... -LocalPort $portRange"
        }
    }
}

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

# 若 .env 配置了国内镜像，提前注入环境变量
$rustupMirror = $Cfg["RUSTUP_DIST_SERVER"] ?? ""
$rustupRoot   = $Cfg["RUSTUP_UPDATE_ROOT"] ?? ""
if ($rustupMirror) { $env:RUSTUP_DIST_SERVER = $rustupMirror }
if ($rustupRoot)   { $env:RUSTUP_UPDATE_ROOT = $rustupRoot }

# 先把 cargo\bin 加入 PATH（rustup 通过 winget 安装后 PATH 需刷新）
$cargoBin = "$env:USERPROFILE\.cargo\bin"
if (Test-Path $cargoBin) { $env:Path = "$cargoBin;$env:Path" }
Refresh-Path

$cargoCmd = Get-Command cargo -ErrorAction SilentlyContinue

if ($cargoCmd) {
    # 检查是否有默认 toolchain，若没有则安装 stable
    $rustcVer = & rustc --version 2>&1 | Out-String
    if ($rustcVer -match "no default is configured" -or $rustcVer -match "error:") {
        Write-Info "rustup 无默认 toolchain，安装 stable..."
        & rustup toolchain install stable 2>&1 | Select-Object -Last 3
        & rustup default stable 2>&1 | Out-Null
        Refresh-Path
        if (Test-Path $cargoBin) { $env:Path = "$cargoBin;$env:Path" }
        $rustcVer = & rustc --version 2>&1 | Out-String
    }
    Write-OK "Rust 已安装: $($rustcVer.Trim())"
} else {
    # 检查 winget 是否已记录安装（避免重复安装时阻塞）
    $wgList = winget list --id Rustlang.Rustup --accept-source-agreements 2>&1 | Out-String
    if ($wgList -match "Rustlang.Rustup") {
        Write-Info "rustup 已安装，但当前会话 PATH 未更新。已将 $cargoBin 加入 PATH"
        # rustup 首次运行会初始化 stable 工具链
        if (Test-Path "$cargoBin\rustup.exe") { & "$cargoBin\rustup.exe" show 2>&1 | Out-Null }
        $cargoCmd = Get-Command cargo -ErrorAction SilentlyContinue
        if ($cargoCmd) { Write-OK "Rust: $(& rustc --version 2>&1)" }
    } else {
        Write-Info "通过 winget 安装 Rust (rustup)..."
        $ok = Install-Winget "Rustlang.Rustup" "Rustup"
        if (Test-Path $cargoBin) { $env:Path = "$cargoBin;$env:Path" }
        Refresh-Path
        $cargoCmd = Get-Command cargo -ErrorAction SilentlyContinue
        if ($cargoCmd) {
            Write-OK "Rust 安装完成: $(& rustc --version 2>&1)"
        } else {
            Write-Warn "Rust 安装后未找到 cargo，请关闭并重开终端后继续"
            Write-Warn "  或手动安装：https://rustup.rs"
        }
    }
}

# 先把 ~/.local/bin 加入 PATH（预编译包安装目录）
$localBin = "$env:USERPROFILE\.local\bin"
if (Test-Path $localBin) { $env:Path = "$localBin;$env:Path" }

$rtkCmd = Get-Command rtk -ErrorAction SilentlyContinue
if ($rtkCmd) {
    Write-OK "rtk 已安装: $(& rtk --version 2>&1)"
} else {
    # 从 GitHub Releases 下载预编译包（比 cargo install 快得多，且无需编译环境）
    Write-Info "安装 rtk (Rust Token Killer) - 下载预编译包..."
    try {
        $rtkVer = "v0.42.0"
        $rtkUrl = "https://github.com/rtk-ai/rtk/releases/download/$rtkVer/rtk-x86_64-pc-windows-msvc.zip"
        New-Item -ItemType Directory -Force -Path $localBin | Out-Null
        $rtkZip = "$env:TEMP\rtk-windows.zip"
        Invoke-WebRequest -Uri $rtkUrl -OutFile $rtkZip -UseBasicParsing
        Expand-Archive -Path $rtkZip -DestinationPath $localBin -Force
        Remove-Item $rtkZip -ErrorAction SilentlyContinue
        $env:Path = "$localBin;$env:Path"
        $rtkCmd = Get-Command rtk -ErrorAction SilentlyContinue
        if ($rtkCmd) {
            Write-OK "rtk 安装完成: $(& rtk --version 2>&1)"
        } else {
            Write-Warn "rtk 安装失败，可手动运行：cargo install --git https://github.com/rtk-ai/rtk"
        }
    } catch {
        Write-Warn "rtk 下载失败: $_"
        Write-Warn "可手动运行：cargo install --git https://github.com/rtk-ai/rtk"
    }
}

# ── rtk init -g：注册 Claude Code PreToolUse hook ─────────────
if (Get-Command rtk -ErrorAction SilentlyContinue) {
    Write-Info "注册 rtk hook 到 Claude Code..."
    # 先禁用遥测，避免交互式确认提示阻塞管道
    $prevEAP = $ErrorActionPreference
    $ErrorActionPreference = "Continue"
    & rtk telemetry disable 2>&1 | Out-Null
    try {
        & rtk init -g --auto-patch 2>&1 | Out-Null
        Write-OK "rtk hook 已注册（重启 Claude Code 后生效）"
    } catch {
        Write-Warn "rtk init 异常（hook 可能已注册）: $_"
    } finally {
        $ErrorActionPreference = $prevEAP
    }

    # ── rtk Copilot Instructions：写入 .github/copilot-instructions.md ──
    # 使用已有的（位于仓库根 .github/），仅在缺失时生成
    $copilotDir = Join-Path $ScriptDir ".." ".github"
    $copilotInst = Join-Path $copilotDir "copilot-instructions.md"
    if (-not (Test-Path $copilotInst)) {
        Write-Info "生成 .github/copilot-instructions.md ..."
        if (-not (Test-Path $copilotDir)) { New-Item -ItemType Directory -Path $copilotDir | Out-Null }
        & rtk init -g --copilot 2>&1 | Out-Null
        Write-OK "Copilot Instructions 已生成"
    } else {
        Write-OK "Copilot Instructions 已存在，跳过生成"
    }
}

# ══════════════════════════════════════════════════════════════
#  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 = @"

# ── Windows Dev Stack (generated by windows-dev-stack\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"
# 将 rtk / cargo 加入 PATH（如尚未在系统 PATH 中）
`$_localBin = "`$env:USERPROFILE\.local\bin"
if ((Test-Path `$_localBin) -and `$env:Path -notlike "*.local\bin*") { `$env:Path = "`$_localBin;`$env:Path" }
`$_cargoBin = "`$env:USERPROFILE\.cargo\bin"
if ((Test-Path `$_cargoBin) -and `$env:Path -notlike "*cargo\bin*") { `$env:Path = "`$_cargoBin;`$env:Path" }
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 "Windows Dev Stack") {
    # 注意：直接用 -replace 会将 $_ 等解释为正则反向引用导致内容爆炸
    # 使用 MatchEvaluator scriptblock 让替换字符串按字面量处理
    $blockCapture = $profileBlock
    $existing = [regex]::Replace($existing, '(?ms)# ── Windows Dev Stack.*?# ─+', { $blockCapture })
    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 "║               ✅  Windows 原生全栈部署完成                   ║" -ForegroundColor Green
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Green
Write-Host ""

$nodeVer   = if (Get-Command node    -ErrorAction SilentlyContinue) { & node    --version 2>&1 } else { "(未安装)" }
$claudeVer = if (Get-Command claude  -ErrorAction SilentlyContinue) { & claude  --version 2>&1 | Select-Object -First 1 } else { "(未安装)" }
$ghVer     = if (Get-Command gh      -ErrorAction SilentlyContinue) { & gh      --version 2>&1 | Select-Object -First 1 } else { "(未安装)" }
$rustVer   = if (Get-Command rustc   -ErrorAction SilentlyContinue) { & rustc   --version 2>&1 } else { "(未安装)" }
$rtkVer    = if (Get-Command rtk     -ErrorAction SilentlyContinue) { & rtk     --version 2>&1 } else { "(未安装)" }

$items = @(
    @("Node.js",       $nodeVer),
    @("Claude Code",   $claudeVer),
    @("GitHub CLI",    $ghVer),
    @("Rust",          $rustVer),
    @("rtk",           $rtkVer),
    @("Unity MCP",     $(if ($unityEntry) { $unityEntry } else { "(未安装)" })),
    @("Godot MCP Pro", $(if ($godotEntry) { $godotEntry } else { "(未安装)" })),
    @("Bridge Port",   "TCP $BridgePort  (防火墙已放行 $BridgePort-$($BridgePort+9))")
)
foreach ($item in $items) {
    $label = $item[0].PadRight(14)
    Write-Host "  $label  $($item[1])" -ForegroundColor White
}

Write-Host ""
Write-Host "  后续手动步骤：" -ForegroundColor Yellow
Write-Host "    1. gh auth login                          # GitHub CLI 登录（首次）"  -ForegroundColor DarkGray
Write-Host "    2. Unity Editor 安装 unity-mcp-plugin："  -ForegroundColor DarkGray
Write-Host "       https://github.com/AnkleBreaker-Studio/unity-mcp-plugin.git" -ForegroundColor DarkGray
Write-Host "    3. Godot 项目：复制 addons/godot_mcp/ 并在 Plugins 中启用" -ForegroundColor DarkGray
Write-Host "       源：$(Join-Path $godotPkgBase "$GodotMcpVersion\addons\godot_mcp")" -ForegroundColor DarkGray
Write-Host "    4. 重启 AI 客户端（Claude Desktop / Cursor / Windsurf / VS Code）" -ForegroundColor DarkGray
Write-Host ""
