# Windows dev launcher - mirror of bash run.sh.
# Spins up backend (uvicorn --reload) + frontend (webpack dev server) + electron
# in one terminal. Hot-reload for Python and React. Ctrl+C to stop all three.
#
# Prereqs: Python 3.12+, Node 20+, npm. Everything else is installed on demand.
#
# Usage:
#   powershell -ExecutionPolicy Bypass -File run.ps1
#   (or)  pwsh run.ps1   if PowerShell 7 is installed.

$ErrorActionPreference = 'Stop'
$ScriptDir = Split-Path -Parent $PSCommandPath

# --- Locate Python ---
$python = $null
foreach ($name in @('python', 'python3', 'py')) {
    $cmd = Get-Command $name -ErrorAction SilentlyContinue
    if ($cmd) { $python = $cmd.Source; break }
}
if (-not $python) {
    throw "Python 3.12+ not found on PATH. Install from https://www.python.org/downloads/"
}

# --- Bundled uv/uvx (matches what build-app-win.ps1 does so MCP discovery works in dev) ---
$UvBinDir = Join-Path $ScriptDir 'backend\uv-bin'
if (-not (Test-Path (Join-Path $UvBinDir 'uvx.exe'))) {
    Write-Host "[setup] Downloading uv/uvx for Windows..."
    New-Item -ItemType Directory -Force -Path $UvBinDir | Out-Null
    $UvUrl = 'https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-pc-windows-msvc.zip'
    $TmpZip = Join-Path $env:TEMP "uv-win-$([guid]::NewGuid()).zip"
    $TmpExtract = Join-Path $env:TEMP "uv-win-extract-$([guid]::NewGuid())"
    try {
        Invoke-WebRequest -Uri $UvUrl -OutFile $TmpZip -UseBasicParsing
        Expand-Archive -Path $TmpZip -DestinationPath $TmpExtract -Force
        Get-ChildItem $TmpExtract -Recurse -Filter 'uv.exe'  | Select-Object -First 1 | ForEach-Object { Copy-Item $_.FullName (Join-Path $UvBinDir 'uv.exe') -Force }
        Get-ChildItem $TmpExtract -Recurse -Filter 'uvx.exe' | Select-Object -First 1 | ForEach-Object { Copy-Item $_.FullName (Join-Path $UvBinDir 'uvx.exe') -Force }
    } finally {
        Remove-Item -Force $TmpZip -ErrorAction SilentlyContinue
        Remove-Item -Recurse -Force $TmpExtract -ErrorAction SilentlyContinue
    }
}

# --- Backend venv + deps ---
$Venv = Join-Path $ScriptDir 'backend\.venv'
$VenvPy = Join-Path $Venv 'Scripts\python.exe'
if (-not (Test-Path $VenvPy)) {
    Write-Host "[setup] Creating Python venv at $Venv ..."
    & $python -m venv $Venv
    if ($LASTEXITCODE -ne 0) { throw "venv creation failed" }
}
Write-Host "[setup] Installing backend deps (idempotent, fast if already up to date)..."
# Suppress PowerShell 5.1's native-stderr-as-error wrapping for these idempotent
# pip calls (deprecation warnings on stderr would otherwise terminate the script
# under $ErrorActionPreference=Stop). We still check $LASTEXITCODE for real failures.
$prevEAP = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
    & $VenvPy -m pip install --quiet --upgrade pip *>&1 | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "pip upgrade failed" }
    & $VenvPy -m pip install --quiet -r (Join-Path $ScriptDir 'backend\requirements.txt')
    if ($LASTEXITCODE -ne 0) { throw "pip install backend reqs failed" }
    & $VenvPy -m pip install --quiet -e (Join-Path $ScriptDir 'debugger')
    if ($LASTEXITCODE -ne 0) { throw "pip install debugger failed" }
} finally {
    $ErrorActionPreference = $prevEAP
}

# --- Frontend deps ---
$FrontendDir = Join-Path $ScriptDir 'frontend'
if (-not (Test-Path (Join-Path $FrontendDir 'node_modules'))) {
    Write-Host "[setup] Installing frontend deps..."
    Push-Location $FrontendDir
    try { & npm install } finally { Pop-Location }
}

# --- Electron deps ---
$ElectronDir = Join-Path $ScriptDir 'electron'
if (-not (Test-Path (Join-Path $ElectronDir 'node_modules'))) {
    Write-Host "[setup] Installing electron deps..."
    Push-Location $ElectronDir
    try { & npm install } finally { Pop-Location }
}

# --- Process tracking + cleanup ---
$script:childPids = New-Object System.Collections.ArrayList

function Stop-Tree($processId, $label) {
    if (-not $processId) { return }
    try {
        & taskkill /PID $processId /T /F 2>$null | Out-Null
        Write-Host "  killed $label (pid $processId)" -ForegroundColor DarkGray
    } catch {}
}

function Cleanup-All {
    Write-Host ""
    Write-Host "Shutting down all services..." -ForegroundColor Yellow
    foreach ($entry in $script:childPids) {
        Stop-Tree $entry.Pid $entry.Label
    }
    Write-Host "All services stopped." -ForegroundColor Green
}

try {
    # --- Start backend (NoNewWindow so logs interleave into this terminal) ---
    # No --reload on Windows: uvicorn's reload mode forces use_subprocess=True
    # which pins the worker to WindowsSelectorEventLoop. That loop raises
    # NotImplementedError on asyncio.create_subprocess_exec — and the Claude
    # Agent SDK uses exactly that to spawn the `claude` CLI, so sending a
    # chat message crashes with "Failed to start Claude Code" under --reload.
    # Mac doesn't hit it (no Proactor/Selector split). Packaged Windows doesn't
    # hit it either (electron/main.js launches uvicorn without --reload).
    # Tradeoff: no backend hot-reload in dev on Windows — Ctrl+C and re-run
    # `.\run.ps1` after backend code changes. Frontend hot-reload is
    # unaffected (webpack-dev-server handles its own watching).
    Write-Host ""
    Write-Host "[backend]  Starting uvicorn on http://localhost:8324 ..." -ForegroundColor Blue
    $backend = Start-Process -PassThru -NoNewWindow `
        -FilePath $VenvPy `
        -WorkingDirectory $ScriptDir `
        -ArgumentList @(
            '-m', 'uvicorn', 'backend.main:app',
            '--host', '127.0.0.1', '--port', '8324'
        )
    [void]$script:childPids.Add(@{ Pid = $backend.Id; Label = 'backend' })

    Write-Host "Waiting for backend (max 90s)..." -ForegroundColor Yellow
    $deadline = (Get-Date).AddSeconds(90)
    $ready = $false
    while ((Get-Date) -lt $deadline) {
        if ($backend.HasExited) { throw "Backend exited prematurely (code $($backend.ExitCode))" }
        try {
            # Hit 127.0.0.1 (not `localhost`) so we don't waste time on the
            # IPv6 ::1 fallback — uvicorn is IPv4-only. TimeoutSec 5 because
            # PS 5.1's first Invoke-WebRequest call pays ~1-2s of .NET
            # network-stack warm-up, and Windows Defender adds scan latency
            # on the first localhost connect from a new process.
            Invoke-WebRequest -Uri 'http://127.0.0.1:8324/api/health/check' -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop | Out-Null
            $ready = $true
            break
        } catch {}
        Start-Sleep -Milliseconds 1500
    }
    if (-not $ready) { throw "Backend did not become ready within 90s" }
    Write-Host "Backend ready." -ForegroundColor Green

    # --- Start frontend ---
    Write-Host ""
    Write-Host "[frontend] Starting webpack dev server on http://localhost:3000 ..." -ForegroundColor Green
    $frontend = Start-Process -PassThru -NoNewWindow `
        -FilePath 'npm.cmd' `
        -WorkingDirectory $FrontendDir `
        -ArgumentList @('run', 'dev')
    [void]$script:childPids.Add(@{ Pid = $frontend.Id; Label = 'frontend' })

    Write-Host "Waiting for frontend (max 90s)..." -ForegroundColor Yellow
    $deadline = (Get-Date).AddSeconds(90)
    $ready = $false
    while ((Get-Date) -lt $deadline) {
        if ($frontend.HasExited) { throw "Frontend exited prematurely (code $($frontend.ExitCode))" }
        try {
            # See backend probe above — same 127.0.0.1 + 5s timeout reasoning.
            Invoke-WebRequest -Uri 'http://127.0.0.1:3000/' -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop | Out-Null
            $ready = $true
            break
        } catch {}
        Start-Sleep -Milliseconds 1500
    }
    if (-not $ready) { throw "Frontend did not become ready within 90s" }
    Write-Host "Frontend ready." -ForegroundColor Green

    # --- Start electron in dev mode (npm run dev = cross-env ELECTRON_DEV=1 electron .) ---
    Write-Host ""
    Write-Host "[electron] Launching dev shell..." -ForegroundColor Magenta
    $electron = Start-Process -PassThru -NoNewWindow `
        -FilePath 'npm.cmd' `
        -WorkingDirectory $ElectronDir `
        -ArgumentList @('run', 'dev')
    [void]$script:childPids.Add(@{ Pid = $electron.Id; Label = 'electron' })

    Write-Host ""
    Write-Host "All services running. Press Ctrl+C to stop." -ForegroundColor Cyan
    Write-Host "  Backend:  http://localhost:8324" -ForegroundColor Blue
    Write-Host "  Frontend: http://localhost:3000" -ForegroundColor Green
    Write-Host "  Electron: dev shell (pid $($electron.Id))" -ForegroundColor Magenta
    Write-Host ""

    # --- Supervise: if any dies, tear down all ---
    while ($true) {
        Start-Sleep -Seconds 3
        if ($backend.HasExited) {
            Write-Host "[backend] exited unexpectedly (code $($backend.ExitCode)). Tearing down..." -ForegroundColor Red
            break
        }
        if ($frontend.HasExited) {
            Write-Host "[frontend] exited unexpectedly (code $($frontend.ExitCode)). Tearing down..." -ForegroundColor Red
            break
        }
        if ($electron.HasExited) {
            Write-Host "[electron] exited (normal close). Tearing down..." -ForegroundColor Yellow
            break
        }
    }
} finally {
    Cleanup-All
}
