[CmdletBinding()]
param(
    # 仅用于自动化验证；正常启动时保持 0，由 Ctrl+C 结束。
    [ValidateRange(0, 3600)]
    [int]$RunForSeconds = 0
)

$ErrorActionPreference = "Stop"

$ProjectRoot = Split-Path -Parent $PSScriptRoot
$FrontendPath = Join-Path $ProjectRoot "frontend"
$BackendPath = Join-Path $ProjectRoot "backend"
$ManagedProcessIds = [System.Collections.Generic.HashSet[int]]::new()
$StartedProcesses = @()

function Get-DevPortListeners {
    param([int]$Port)

    return @(
        Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue
    )
}

function Test-DevPortListening {
    param([int]$Port)

    $Endpoints = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpListeners()
    return $null -ne ($Endpoints | Where-Object { $_.Port -eq $Port } | Select-Object -First 1)
}

function Assert-DevPortAvailable {
    param(
        [int]$Port,
        [string]$ApplicationName
    )

    if (-not (Test-DevPortListening -Port $Port)) {
        return
    }

    # 只有端口确实被占用时才调用较慢的 Windows 连接查询，以便展示 PID。
    $Listeners = Get-DevPortListeners -Port $Port

    $Owners = foreach ($Listener in $Listeners) {
        $Process = Get-CimInstance Win32_Process `
            -Filter "ProcessId=$($Listener.OwningProcess)" `
            -ErrorAction SilentlyContinue
        if ($null -ne $Process) {
            "PID $($Process.ProcessId)：$($Process.CommandLine)"
        }
        else {
            "PID $($Listener.OwningProcess)"
        }
    }
    throw "$ApplicationName 端口 $Port 已被占用。请先结束以下进程后重试：`n$($Owners -join "`n")"
}

function Update-ManagedProcessTree {
    # Uvicorn reload、pnpm 和 Vite 都会派生子进程。持续记录完整进程树，
    # 即使某个中间父进程先退出，最终清理时仍能找到它已经创建的子进程。
    # Win32_Process 单次查询在部分 Windows 机器上约需半秒，逐个父 PID
    # 查询会明显拖慢启动。因此每轮只取一次快照，在内存中展开进程树。
    $AllProcesses = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue)
    $AddedProcess = $true
    while ($AddedProcess) {
        $AddedProcess = $false
        foreach ($Child in $AllProcesses) {
            if (-not $ManagedProcessIds.Contains([int]$Child.ParentProcessId)) {
                continue
            }

            $ChildId = [int]$Child.ProcessId
            if ($ManagedProcessIds.Add($ChildId)) {
                $AddedProcess = $true
            }
        }
    }
}

function Test-ProcessRunning {
    param([System.Diagnostics.Process]$Process)

    try {
        $Process.Refresh()
        return -not $Process.HasExited
    }
    catch {
        return $false
    }
}

function Test-HttpEndpoint {
    param([string]$Uri)

    try {
        $Response = Invoke-WebRequest `
            -Uri $Uri `
            -UseBasicParsing `
            -TimeoutSec 2 `
            -ErrorAction Stop
        return $Response.StatusCode -ge 200 -and $Response.StatusCode -lt 500
    }
    catch {
        return $false
    }
}

function Wait-ForDevelopmentServices {
    param(
        [System.Diagnostics.Process]$FrontendProcess,
        [System.Diagnostics.Process]$BackendProcess,
        [int]$TimeoutSeconds = 45
    )

    $Deadline = (Get-Date).AddSeconds($TimeoutSeconds)
    while ((Get-Date) -lt $Deadline) {
        Update-ManagedProcessTree

        if (-not (Test-ProcessRunning -Process $FrontendProcess)) {
            throw "前端开发服务器启动失败，请检查上方 pnpm 输出。"
        }
        if (-not (Test-ProcessRunning -Process $BackendProcess)) {
            throw "后端开发服务器启动失败，请检查上方 Uvicorn 输出。"
        }

        $H5Ready = Test-HttpEndpoint -Uri "http://127.0.0.1:5173/"
        $AdminReady = Test-HttpEndpoint -Uri "http://127.0.0.1:5174/"
        $BackendReady = Test-HttpEndpoint -Uri "http://127.0.0.1:8000/docs"
        if ($H5Ready -and $AdminReady -and $BackendReady) {
            return
        }

        Start-Sleep -Milliseconds 300
    }

    throw "开发服务在 $TimeoutSeconds 秒内未全部就绪，请检查上方启动日志。"
}

function Stop-ManagedProcesses {
    Write-Host "`n正在停止智保通开发服务..." -ForegroundColor Yellow

    # 多刷新几次，覆盖清理开始前恰好生成的 reload/Vite 子进程。
    1..3 | ForEach-Object {
        Update-ManagedProcessTree
        Start-Sleep -Milliseconds 100
    }

    $Processes = @(
        Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
            Where-Object {
                $_.ProcessId -ne $PID -and
                $ManagedProcessIds.Contains([int]$_.ProcessId)
            }
    )

    # 子进程通常晚于父进程创建；倒序停止可避免父进程退出后留下孤儿。
    $Processes |
        Sort-Object CreationDate -Descending |
        ForEach-Object {
            Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
        }

    # 确认本次脚本启动的三个监听端口已经释放。
    $Deadline = (Get-Date).AddSeconds(5)
    while ((Get-Date) -lt $Deadline) {
        $AnyPortListening = $false
        foreach ($Port in 5173, 5174, 8000) {
            if (Test-DevPortListening -Port $Port) {
                $AnyPortListening = $true
                break
            }
        }
        if (-not $AnyPortListening) {
            break
        }
        Start-Sleep -Milliseconds 200
    }

    Write-Host "智保通开发服务已停止。" -ForegroundColor Green
}

foreach ($Service in @(
    @{ Port = 5173; Name = "H5" },
    @{ Port = 5174; Name = "管理后台" },
    @{ Port = 8000; Name = "后端 API" }
)) {
    Assert-DevPortAvailable -Port $Service.Port -ApplicationName $Service.Name
}

$PnpmPath = (Get-Command pnpm.cmd -ErrorAction Stop).Source
$UvPath = (Get-Command uv.exe -ErrorAction Stop).Source

try {
    Write-Host "正在启动 H5、管理后台和后端 API..." -ForegroundColor Cyan

    $FrontendProcess = Start-Process `
        -FilePath $PnpmPath `
        -ArgumentList "dev" `
        -WorkingDirectory $FrontendPath `
        -NoNewWindow `
        -PassThru
    $StartedProcesses += $FrontendProcess
    [void]$ManagedProcessIds.Add($FrontendProcess.Id)

    $BackendProcess = Start-Process `
        -FilePath $UvPath `
        -ArgumentList @(
            "run", "uvicorn", "zbt.main:app",
            "--host", "127.0.0.1",
            "--port", "8000",
            "--reload"
        ) `
        -WorkingDirectory $BackendPath `
        -NoNewWindow `
        -PassThru
    $StartedProcesses += $BackendProcess
    [void]$ManagedProcessIds.Add($BackendProcess.Id)

    Wait-ForDevelopmentServices `
        -FrontendProcess $FrontendProcess `
        -BackendProcess $BackendProcess

    Write-Host ""
    Write-Host "智保通开发服务已全部启动：" -ForegroundColor Green
    Write-Host "H5：http://127.0.0.1:5173"
    Write-Host "管理后台：http://127.0.0.1:5174"
    Write-Host "后端 API：http://127.0.0.1:8000/docs"
    Write-Host "按 Ctrl+C 可一次性停止全部服务。" -ForegroundColor DarkGray

    $ReadyAt = Get-Date
    while ($true) {
        Update-ManagedProcessTree

        foreach ($StartedProcess in $StartedProcesses) {
            if (-not (Test-ProcessRunning -Process $StartedProcess)) {
                throw "开发服务进程 PID $($StartedProcess.Id) 意外退出，请检查上方日志。"
            }
        }

        if ($RunForSeconds -gt 0 -and
            ((Get-Date) - $ReadyAt).TotalSeconds -ge $RunForSeconds) {
            break
        }

        Start-Sleep -Milliseconds 500
    }
}
finally {
    Stop-ManagedProcesses
}
