# -*- coding: utf-8 -*-
# 启动管理后台（后端 + 前端）
# 服务成功启动后自动打开浏览器

$ErrorActionPreference = "Stop"

try {
    # CHANGE 2026-03-07 | 定位项目根目录：从 bat 启动目录推算，不穿透 junction
    # 背景：C:\Project\NeoZQYY 是 junction → D:\NeoZQYY\...\repo，
    # $MyInvocation.MyCommand.Path 和 Split-Path 都会穿透 junction 解析到 D 盘，
    # 导致后端 CWD、.venv python、.env 全部指向 D 盘副本。
    # 解决：优先用环境变量 NEOZQYY_ROOT；其次用 bat 传入的 %~dp0（不穿透 junction）；
    # 最后才回退到脚本路径推算。
    if ($env:NEOZQYY_ROOT -and (Test-Path $env:NEOZQYY_ROOT)) {
        $ProjectRoot = $env:NEOZQYY_ROOT
    } elseif ($env:NEOZQYY_LAUNCH_DIR -and (Test-Path $env:NEOZQYY_LAUNCH_DIR)) {
        # 由 start-admin.bat 通过 %~dp0 注入，不穿透 junction
        $ProjectRoot = $env:NEOZQYY_LAUNCH_DIR.TrimEnd('\')
    } elseif ($MyInvocation.MyCommand.Path) {
        $ProjectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path))
    } else {
        $ProjectRoot = $PWD.Path
    }

    # CHANGE 2026-03-07 | 将 ProjectRoot 注入环境变量 NEOZQYY_ROOT，
    # 使 config.py 的 _find_project_root() 策略 1 直接命中，
    # 不再依赖 __file__ 推算（__file__ 会穿透 junction 到 D 盘）。
    # 这个环境变量会被子进程（uvicorn）继承。
    $env:NEOZQYY_ROOT = $ProjectRoot

    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host "  NeoZQYY 管理后台启动脚本" -ForegroundColor Cyan
    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "项目根目录: $ProjectRoot"
    Write-Host "NEOZQYY_ROOT=$env:NEOZQYY_ROOT"
    Write-Host ""

    $backendDir = Join-Path $ProjectRoot "apps\backend"
    $frontendDir = Join-Path $ProjectRoot "apps\admin-web"
    $tenantDir  = Join-Path $ProjectRoot "apps\tenant-admin"
    if (-not (Test-Path $backendDir)) { throw "后端目录不存在: $backendDir" }
    if (-not (Test-Path $frontendDir)) { throw "前端目录不存在: $frontendDir" }
    if (-not (Test-Path $tenantDir))  { throw "租户管理后台目录不存在: $tenantDir" }

    # ── 端口冲突检测：确保 8000/5173/5174 未被旧进程占用 ──
    foreach ($port in @(8000, 5173, 5174)) {
        $listeners = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
        if ($listeners) {
            foreach ($l in $listeners) {
                $procId = $l.OwningProcess
                $proc = Get-Process -Id $procId -ErrorAction SilentlyContinue
                $procPath = if ($proc) { $proc.Path } else { "未知" }
                Write-Host "端口 $port 被占用: PID=$procId ($procPath)" -ForegroundColor Yellow

                # 尝试多种方式终止：Stop-Process → taskkill /T /F
                Write-Host "  正在终止旧进程..." -ForegroundColor Yellow
                Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
                Start-Sleep -Seconds 1
                # 如果 Stop-Process 不够，用 taskkill 连子进程一起杀
                # taskkill 非零退出码不应终止脚本
                $ErrorActionPreference = "Continue"
                taskkill /PID $procId /T /F 2>$null | Out-Null
                $ErrorActionPreference = "Stop"
            }

            # 等待端口释放（最多 30 秒）
            $waitMax = 30
            $waited = 0
            while ($waited -lt $waitMax) {
                Start-Sleep -Seconds 2
                $waited += 2
                $still = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
                if (-not $still) {
                    Write-Host "  端口 $port 已释放（等待 ${waited}s）" -ForegroundColor Green
                    break
                }
                # 父进程可能已死但子进程仍占端口，扫描并杀子进程
                $zombiePid = $still.OwningProcess
                $children = Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $zombiePid }
                if ($children) {
                    foreach ($child in $children) {
                        Write-Host "  发现残留子进程: PID=$($child.ProcessId) ($($child.ExecutablePath))" -ForegroundColor Yellow
                        # taskkill 非零退出码不应终止脚本
                        $ErrorActionPreference = "Continue"
                        taskkill /PID $child.ProcessId /T /F 2>$null | Out-Null
                        $ErrorActionPreference = "Stop"
                    }
                }
                Write-Host "  端口 $port 仍被占用，继续等待... (${waited}/${waitMax}s)" -ForegroundColor Yellow
            }
            if ($waited -ge $waitMax) {
                $still2 = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
                if ($still2) {
                    Write-Host ""
                    Write-Host "  !! 端口 $port 无法释放（僵尸进程 PID=$($still2.OwningProcess)）" -ForegroundColor Red
                    Write-Host "  !! 请重启电脑或手动执行: netsh int ipv4 reset" -ForegroundColor Red
                    Write-Host "  !! 或等待几分钟后重试" -ForegroundColor Red
                    throw "端口 $port 被僵尸进程占用，无法启动"
                }
            }
        }
    }

    # 前端日志文件（每次用唯一文件名，避免上次进程锁定旧文件）
    $ts = Get-Date -Format "yyyyMMdd_HHmmss"
    $frontendLog = Join-Path $env:TEMP "neozqyy_fe_${ts}.log"
    $tenantLog   = Join-Path $env:TEMP "neozqyy_ta_${ts}.log"

    # 选择 PowerShell 可执行文件：优先 pwsh (7+)，回退 powershell (5.1)
    $psExe = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" }

    # ── 生成临时启动脚本（带时间戳，避免旧脚本残留） ──
    $beTmp = Join-Path $env:TEMP "neozqyy_start_be_${ts}.ps1"
    $feTmp = Join-Path $env:TEMP "neozqyy_start_fe_${ts}.ps1"
    $taTmp = Join-Path $env:TEMP "neozqyy_start_ta_${ts}.ps1"

    $q = [char]39
    # CHANGE 2026-03-07 | 显式使用 .venv 的 python，避免 uv run 解析到 miniconda
    # 背景：uv run uvicorn 在此机器上解析到 C:\ProgramData\miniconda3\python.exe，
    # 导致 config.py 的 Path(__file__).resolve() 指向错误的代码副本（D 盘）
    $venvPython = Join-Path $ProjectRoot ".venv\Scripts\python.exe"
    if (-not (Test-Path $venvPython)) {
        throw ".venv Python 不存在: $venvPython — 请先运行 uv sync"
    }

    # CHANGE 2026-03-07 | 临时脚本中显式注入 NEOZQYY_ROOT，确保 uvicorn 子进程
    # 无论通过何种方式启动都能拿到正确的项目根目录
    # CHANGE 2026-03-07 | 解决 ANSI 转义码乱码：
    # PowerShell 5.1 + 旧版 conhost 不解析 ANSI 转义序列，uvicorn 彩色日志
    # 显示为 [32m、[0m 等乱码。设置 NO_COLOR=1 禁用颜色输出。
    # pwsh 7+ 和 Windows Terminal 原生支持 VT，不受影响。
    # CHANGE 2026-04-01 | 修复三个显示问题：
    # 1. Write-Host 用单引号包裹导致 $env:NEOZQYY_ROOT 不展开 → 改用双引号
    # 2. NO_COLOR 对 uvicorn 不生效 → 加 --no-color 参数
    # 3. PS 5.1 传统控制台 UTF-8 箭头乱码 → 所有临时脚本开头 chcp 65001
    #    并统一设 NO_COLOR=1 禁用 ANSI 转义码
    $beLines = @(
        "`$env:NEOZQYY_ROOT = ${q}${ProjectRoot}${q}"
        ""
        "# PS 5.1 + 传统控制台：启用 UTF-8 代码页 + 禁用 ANSI 颜色"
        "if (`$PSVersionTable.PSVersion.Major -lt 7 -and -not `$env:WT_SESSION) {"
        "    chcp 65001 | Out-Null"
        "    `$env:NO_COLOR = ${q}1${q}"
        "    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8"
        "    Write-Host ${q}[提示] PS 5.1 + 传统控制台，已启用 UTF-8 并禁用 ANSI 颜色${q} -ForegroundColor Yellow"
        "}"
        ""
        "Set-Location -LiteralPath ${q}${backendDir}${q}"
        "Write-Host ${q}=== 后端 FastAPI ===${q} -ForegroundColor Green"
        "Write-Host `"NEOZQYY_ROOT=`$env:NEOZQYY_ROOT`""
        "& ${q}${venvPython}${q} -m uvicorn app.main:app --reload --port 8000 --no-use-colors"
        "Write-Host ${q}后端已退出，按任意键关闭...${q} -ForegroundColor Red"
        "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})"
    )
    $beLines | Set-Content -Path $beTmp -Encoding UTF8

    $feLines = @(
        "# PS 5.1 + 传统控制台：启用 UTF-8 + 禁用 ANSI"
        "if (`$PSVersionTable.PSVersion.Major -lt 7 -and -not `$env:WT_SESSION) {"
        "    chcp 65001 | Out-Null"
        "    `$env:NO_COLOR = ${q}1${q}"
        "    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8"
        "}"
        ""
        "Set-Location -LiteralPath ${q}${frontendDir}${q}"
        "Write-Host ${q}=== 前端 Vite (admin-web) ===${q} -ForegroundColor Green"
        "cmd /c ${q}pnpm dev 2>&1${q} | Tee-Object -FilePath ${q}${frontendLog}${q}"
        "Write-Host ${q}前端已退出，按任意键关闭...${q} -ForegroundColor Red"
        "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})"
    )
    $feLines | Set-Content -Path $feTmp -Encoding UTF8

    $taLines = @(
        "# PS 5.1 + 传统控制台：启用 UTF-8 + 禁用 ANSI"
        "if (`$PSVersionTable.PSVersion.Major -lt 7 -and -not `$env:WT_SESSION) {"
        "    chcp 65001 | Out-Null"
        "    `$env:NO_COLOR = ${q}1${q}"
        "    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8"
        "}"
        ""
        "Set-Location -LiteralPath ${q}${tenantDir}${q}"
        "Write-Host ${q}=== 租户管理后台 Vite (tenant-admin) ===${q} -ForegroundColor Green"
        "cmd /c ${q}pnpm dev 2>&1${q} | Tee-Object -FilePath ${q}${tenantLog}${q}"
        "Write-Host ${q}租户管理后台已退出，按任意键关闭...${q} -ForegroundColor Red"
        "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})"
    )
    $taLines | Set-Content -Path $taTmp -Encoding UTF8

    # ── 启动服务函数 ──
    function Start-AllServices {
        param([ref]$BeProc, [ref]$FeProc, [ref]$TaProc)

        Write-Host "[1/3] 启动后端 FastAPI (http://localhost:8000) ..." -ForegroundColor Yellow
        $BeProc.Value = Start-Process $psExe -ArgumentList "-NoExit", "-ExecutionPolicy", "Bypass", "-File", $beTmp -PassThru

        Start-Sleep -Seconds 2

        Write-Host "[2/3] 启动前端 Vite admin-web (http://localhost:5173) ..." -ForegroundColor Yellow
        $FeProc.Value = Start-Process $psExe -ArgumentList "-NoExit", "-ExecutionPolicy", "Bypass", "-File", $feTmp -PassThru

        Start-Sleep -Seconds 1

        Write-Host "[3/3] 启动租户管理后台 Vite tenant-admin (http://localhost:5174) ..." -ForegroundColor Yellow
        $TaProc.Value = Start-Process $psExe -ArgumentList "-NoExit", "-ExecutionPolicy", "Bypass", "-File", $taTmp -PassThru

        Write-Host ""
        Write-Host "========================================" -ForegroundColor Green
        Write-Host "  三个服务已在新窗口中启动" -ForegroundColor Green
        Write-Host "  后端:         http://localhost:8000" -ForegroundColor Green
        Write-Host "  管理后台:     http://localhost:5173" -ForegroundColor Green
        Write-Host "  租户管理后台: http://localhost:5174" -ForegroundColor Green
        Write-Host "========================================" -ForegroundColor Green
        Write-Host ""
    }

    # ── 终止服务函数 ──
    function Stop-AllServices {
        param($BeProc, $FeProc, $TaProc)

        Write-Host ""
        Write-Host "正在终止所有服务..." -ForegroundColor Yellow
        $ErrorActionPreference = "Continue"
        foreach ($info in @(
            @{ Name = "后端 FastAPI";    Proc = $BeProc; Port = 8000 },
            @{ Name = "前端 admin-web";  Proc = $FeProc; Port = 5173 },
            @{ Name = "租户 tenant-admin"; Proc = $TaProc; Port = 5174 }
        )) {
            $p = $info.Proc
            if ($p -and -not $p.HasExited) {
                Write-Host "  终止 $($info.Name) (PID=$($p.Id))..." -ForegroundColor Yellow
                taskkill /PID $p.Id /T /F 2>$null | Out-Null
            }
            # 同时按端口清理（防止子进程残留）
            $listeners = Get-NetTCPConnection -LocalPort $info.Port -State Listen -ErrorAction SilentlyContinue
            foreach ($l in $listeners) {
                taskkill /PID $l.OwningProcess /T /F 2>$null | Out-Null
            }
        }
        $ErrorActionPreference = "Stop"
        Start-Sleep -Seconds 1
        Write-Host "  所有服务已终止" -ForegroundColor Green
    }

    # ── 等待前端就绪 + 打开浏览器 ──
    function Wait-AndOpenBrowser {
        Write-Host "等待前端 Vite 就绪..." -ForegroundColor Yellow
        $timeout = 45
        $elapsed = 0
        $feReady = $false
        $taReady = $false

        while ($elapsed -lt $timeout -and (-not $feReady -or -not $taReady)) {
            Start-Sleep -Seconds 1
            $elapsed++
            if (-not $feReady -and (Test-Path $frontendLog)) {
                $raw = Get-Content $frontendLog -Raw -ErrorAction SilentlyContinue
                if ($raw) {
                    $clean = $raw -replace '\x1b\[[0-9;]*m', ''
                    if ($clean -match "localhost:5173" -or $clean -match "ready in") {
                        $feReady = $true
                        Write-Host "  admin-web 已就绪（${elapsed}s）" -ForegroundColor Green
                    }
                }
            }
            if (-not $taReady -and (Test-Path $tenantLog)) {
                $raw = Get-Content $tenantLog -Raw -ErrorAction SilentlyContinue
                if ($raw) {
                    $clean = $raw -replace '\x1b\[[0-9;]*m', ''
                    if ($clean -match "localhost:5174" -or $clean -match "ready in") {
                        $taReady = $true
                        Write-Host "  tenant-admin 已就绪（${elapsed}s）" -ForegroundColor Green
                    }
                }
            }
        }

        if ($feReady) { Start-Process "http://localhost:5173" }
        else { Write-Host "admin-web 等待超时，请手动打开 http://localhost:5173" -ForegroundColor Red }
        if ($taReady) { Start-Process "http://localhost:5174" }
        else { Write-Host "tenant-admin 等待超时，请手动打开 http://localhost:5174" -ForegroundColor Red }
    }

    # ── 倒计时函数 ──
    function Show-Countdown {
        param([int]$Seconds)
        for ($i = $Seconds; $i -ge 1; $i--) {
            Write-Host "`r  重启倒计时: ${i}s ...  " -NoNewline -ForegroundColor Cyan
            Start-Sleep -Seconds 1
        }
        Write-Host "`r  重启倒计时: 0s — 开始启动!   " -ForegroundColor Green
        Write-Host ""
    }

    # ── 首次启动 ──
    $beProc = $null; $feProc = $null; $taProc = $null
    Start-AllServices -BeProc ([ref]$beProc) -FeProc ([ref]$feProc) -TaProc ([ref]$taProc)
    Wait-AndOpenBrowser

    # ── 交互菜单循环 ──
    $running = $true
    while ($running) {
        Write-Host ""
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host "  操作菜单" -ForegroundColor Cyan
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host "  [1] 终止所有服务" -ForegroundColor Yellow
        Write-Host "  [2] 重启所有服务（间隔 5 秒倒计时）" -ForegroundColor Yellow
        Write-Host "  [3] 退出（终止服务并关闭窗口）" -ForegroundColor Yellow
        Write-Host "========================================" -ForegroundColor Cyan
        $choice = Read-Host "请输入选项 (1/2/3)"

        switch ($choice) {
            "1" {
                Stop-AllServices -BeProc $beProc -FeProc $feProc -TaProc $taProc
                $beProc = $null; $feProc = $null; $taProc = $null
            }
            "2" {
                Stop-AllServices -BeProc $beProc -FeProc $feProc -TaProc $taProc
                Show-Countdown -Seconds 5
                # 重新生成日志文件名（避免旧文件锁定）
                $ts = Get-Date -Format "yyyyMMdd_HHmmss"
                $frontendLog = Join-Path $env:TEMP "neozqyy_fe_${ts}.log"
                $tenantLog   = Join-Path $env:TEMP "neozqyy_ta_${ts}.log"
                # 重新生成临时启动脚本
                $beLines = @(
                    "`$env:NEOZQYY_ROOT = ${q}${ProjectRoot}${q}"
                    ""
                    "if (`$PSVersionTable.PSVersion.Major -lt 7 -and -not `$env:WT_SESSION) {"
                    "    chcp 65001 | Out-Null"
                    "    `$env:NO_COLOR = ${q}1${q}"
                    "    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8"
                    "    Write-Host ${q}[提示] PS 5.1 + 传统控制台，已启用 UTF-8 并禁用 ANSI 颜色${q} -ForegroundColor Yellow"
                    "}"
                    ""
                    "Set-Location -LiteralPath ${q}${backendDir}${q}"
                    "Write-Host ${q}=== 后端 FastAPI ===${q} -ForegroundColor Green"
                    "Write-Host `"NEOZQYY_ROOT=`$env:NEOZQYY_ROOT`""
                    "& ${q}${venvPython}${q} -m uvicorn app.main:app --reload --port 8000 --no-use-colors"
                    "Write-Host ${q}后端已退出，按任意键关闭...${q} -ForegroundColor Red"
                    "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})"
                )
                $beLines | Set-Content -Path $beTmp -Encoding UTF8
                $feLines = @(
                    "if (`$PSVersionTable.PSVersion.Major -lt 7 -and -not `$env:WT_SESSION) {"
                    "    chcp 65001 | Out-Null"
                    "    `$env:NO_COLOR = ${q}1${q}"
                    "    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8"
                    "}"
                    ""
                    "Set-Location -LiteralPath ${q}${frontendDir}${q}"
                    "Write-Host ${q}=== 前端 Vite (admin-web) ===${q} -ForegroundColor Green"
                    "cmd /c ${q}pnpm dev 2>&1${q} | Tee-Object -FilePath ${q}${frontendLog}${q}"
                    "Write-Host ${q}前端已退出，按任意键关闭...${q} -ForegroundColor Red"
                    "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})"
                )
                $feLines | Set-Content -Path $feTmp -Encoding UTF8
                $taLines = @(
                    "if (`$PSVersionTable.PSVersion.Major -lt 7 -and -not `$env:WT_SESSION) {"
                    "    chcp 65001 | Out-Null"
                    "    `$env:NO_COLOR = ${q}1${q}"
                    "    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8"
                    "}"
                    ""
                    "Set-Location -LiteralPath ${q}${tenantDir}${q}"
                    "Write-Host ${q}=== 租户管理后台 Vite (tenant-admin) ===${q} -ForegroundColor Green"
                    "cmd /c ${q}pnpm dev 2>&1${q} | Tee-Object -FilePath ${q}${tenantLog}${q}"
                    "Write-Host ${q}租户管理后台已退出，按任意键关闭...${q} -ForegroundColor Red"
                    "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})"
                )
                $taLines | Set-Content -Path $taTmp -Encoding UTF8

                $beProc = $null; $feProc = $null; $taProc = $null
                Start-AllServices -BeProc ([ref]$beProc) -FeProc ([ref]$feProc) -TaProc ([ref]$taProc)
                Wait-AndOpenBrowser
            }
            "3" {
                Stop-AllServices -BeProc $beProc -FeProc $feProc -TaProc $taProc
                $running = $false
            }
            default {
                Write-Host "  无效选项，请输入 1、2 或 3" -ForegroundColor Red
            }
        }
    }

} catch {
    Write-Host ""
    Write-Host "启动失败: $_" -ForegroundColor Red
    Write-Host $_.ScriptStackTrace -ForegroundColor DarkRed
}

Write-Host ""
Write-Host "按任意键关闭此窗口..." -ForegroundColor DarkGray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
