# test_startup_perf.ps1 — Startup & config loading performance measurements
#
# Measures concrete numbers:
#   1. Server startup time (new-session -d until port file appears)
#   2. Config loading with plugins (time from server start to options applied)
#   3. pwsh first-prompt latency inside psmux pane
#   4. Comparison: bare pwsh startup vs psmux+pwsh startup
#   5. TCP command round-trip latency
#   6. Config with N plugins — scaling
#   7. Comparison baselines with Windows Terminal where applicable

$ErrorActionPreference = "Continue"
$script:TestsPassed = 0
$script:TestsFailed = 0
$script:TestsSkipped = 0

function Write-Pass { param($msg) Write-Host "[PASS] $msg" -ForegroundColor Green; $script:TestsPassed++ }
function Write-Fail { param($msg) Write-Host "[FAIL] $msg" -ForegroundColor Red; $script:TestsFailed++ }
function Write-Skip { param($msg) Write-Host "[SKIP] $msg" -ForegroundColor Yellow; $script:TestsSkipped++ }
function Write-Info { param($msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan }
function Write-Test { param($msg) Write-Host "[TEST] $msg" -ForegroundColor White }
function Write-Perf { param($msg) Write-Host "[PERF] $msg" -ForegroundColor Magenta }

$PSMUX = "$PSScriptRoot\..\target\release\psmux.exe"
if (-not (Test-Path $PSMUX)) { Write-Error "psmux release binary not found. Run: cargo build --release"; exit 1 }
Write-Info "Using: $PSMUX"

$HOME_DIR = $env:USERPROFILE
$PSMUX_DIR = "$HOME_DIR\.psmux"
$PLUGINS_DIR = "$PSMUX_DIR\plugins"

function Reset-Psmux {
    Get-Process psmux -ErrorAction SilentlyContinue | Stop-Process -Force
    Start-Sleep -Milliseconds 800
    Remove-Item "$PSMUX_DIR\*.port" -Force -ErrorAction SilentlyContinue
    Remove-Item "$PSMUX_DIR\*.key" -Force -ErrorAction SilentlyContinue
}

Write-Host ""
Write-Host ("=" * 76)
Write-Host "       PSMUX STARTUP & PERFORMANCE MEASUREMENT SUITE"
Write-Host ("=" * 76)
Write-Host ""

# ===========================================================================
# MEASUREMENT 1: Bare server startup (no config, detached)
# ===========================================================================
Write-Host ("=" * 70)
Write-Host "  1. BARE SERVER STARTUP (no config)"
Write-Host ("=" * 70)

$startupTimes = @()
for ($i = 0; $i -lt 5; $i++) {
    Reset-Psmux
    $portFile = "$PSMUX_DIR\bare_$i.port"
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $env:PSMUX_CONFIG_FILE = "NUL"  # empty config
    Start-Process -FilePath $PSMUX -ArgumentList "new-session -s bare_$i -d" -WindowStyle Hidden
    $env:PSMUX_CONFIG_FILE = $null
    # Poll for port file
    for ($j = 0; $j -lt 500; $j++) {
        if (Test-Path $portFile) { break }
        Start-Sleep -Milliseconds 10
    }
    $sw.Stop()
    if (Test-Path $portFile) {
        $startupTimes += $sw.ElapsedMilliseconds
        Write-Info "  Run $($i+1): $($sw.ElapsedMilliseconds)ms"
    } else {
        Write-Info "  Run $($i+1): TIMEOUT (port file not created)"
    }
}

if ($startupTimes.Count -gt 0) {
    $avg = [math]::Round(($startupTimes | Measure-Object -Average).Average, 1)
    $min = ($startupTimes | Measure-Object -Minimum).Minimum
    $max = ($startupTimes | Measure-Object -Maximum).Maximum
    Write-Perf "Bare server startup: avg=${avg}ms  min=${min}ms  max=${max}ms  (n=$($startupTimes.Count))"
    if ($avg -lt 2000) { Write-Pass "Server startup under 2s" }
    elseif ($avg -lt 4000) { Write-Pass "Server startup under 4s (acceptable)" }
    else { Write-Fail "Server startup too slow: ${avg}ms" }
} else {
    Write-Fail "No successful bare startup measurements"
}

# ===========================================================================
# MEASUREMENT 2: Server startup WITH plugins (sensible + gruvbox)
# ===========================================================================
Write-Host ""
Write-Host ("=" * 70)
Write-Host "  2. SERVER STARTUP WITH PLUGINS (sensible + gruvbox)"
Write-Host ("=" * 70)

$pluginConf = "$env:TEMP\psmux_perf_plugins.conf"
Set-Content -Path $pluginConf -Value @"
set -g @plugin 'psmux-plugins/psmux-sensible'
set -g @plugin 'psmux-plugins/psmux-theme-gruvbox'
set -g automatic-rename off
"@ -Encoding UTF8

$pluginTimes = @()
for ($i = 0; $i -lt 5; $i++) {
    Reset-Psmux
    $sess = "plug_$i"
    $portFile = "$PSMUX_DIR\$sess.port"
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $env:PSMUX_CONFIG_FILE = $pluginConf
    Start-Process -FilePath $PSMUX -ArgumentList "new-session -s $sess -d" -WindowStyle Hidden
    $env:PSMUX_CONFIG_FILE = $null
    for ($j = 0; $j -lt 500; $j++) {
        if (Test-Path $portFile) { break }
        Start-Sleep -Milliseconds 10
    }
    $sw.Stop()
    if (Test-Path $portFile) {
        $pluginTimes += $sw.ElapsedMilliseconds
        Write-Info "  Run $($i+1): $($sw.ElapsedMilliseconds)ms"
    } else {
        Write-Info "  Run $($i+1): TIMEOUT"
    }
}

if ($pluginTimes.Count -gt 0) {
    $avg = [math]::Round(($pluginTimes | Measure-Object -Average).Average, 1)
    $min = ($pluginTimes | Measure-Object -Minimum).Minimum
    $max = ($pluginTimes | Measure-Object -Maximum).Maximum
    Write-Perf "Plugin startup: avg=${avg}ms  min=${min}ms  max=${max}ms  (n=$($pluginTimes.Count))"

    if ($startupTimes.Count -gt 0) {
        $bareAvg = [math]::Round(($startupTimes | Measure-Object -Average).Average, 1)
        $overhead = [math]::Round($avg - $bareAvg, 1)
        Write-Perf "Plugin overhead: ${overhead}ms (${avg}ms - ${bareAvg}ms bare)"
    }
} else {
    Write-Fail "No successful plugin startup measurements"
}

# ===========================================================================
# MEASUREMENT 3: Plugin options verified immediately after startup
# ===========================================================================
Write-Host ""
Write-Host ("=" * 70)
Write-Host "  3. PLUGIN SETTINGS APPLIED AT STARTUP (synchronous)"
Write-Host ("=" * 70)

Reset-Psmux
$sess = "verify_sync"
$env:PSMUX_CONFIG_FILE = $pluginConf
Start-Process -FilePath $PSMUX -ArgumentList "new-session -s $sess -d" -WindowStyle Hidden
$env:PSMUX_CONFIG_FILE = $null
Start-Sleep -Seconds 3

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$style = (& $PSMUX show-options -g -v status-style -t $sess 2>&1 | Out-String).Trim()
$autorename = (& $PSMUX show-options -g -v automatic-rename -t $sess 2>&1 | Out-String).Trim()
$escape = (& $PSMUX show-options -g -v escape-time -t $sess 2>&1 | Out-String).Trim()
$sw.Stop()

if ($style -match "#3c3836|#ebdbb2") { Write-Pass "Gruvbox theme loaded synchronously (status-style='$style')" }
else { Write-Fail "Theme not sync: status-style='$style'" }

if ($autorename -eq "off") { Write-Pass "User override preserved (automatic-rename=off)" }
else { Write-Fail "User override lost (automatic-rename='$autorename')" }

if ($escape -eq "50") { Write-Pass "Sensible default applied (escape-time=50)" }
else { Write-Fail "Sensible not applied: escape-time='$escape'" }

Write-Perf "3 option queries: $($sw.ElapsedMilliseconds)ms"

# ===========================================================================
# MEASUREMENT 4: pwsh first-prompt latency (bare vs psmux)
# ===========================================================================
Write-Host ""
Write-Host ("=" * 70)
Write-Host "  4. PWSH FIRST-PROMPT LATENCY"
Write-Host ("=" * 70)

# 4a: Bare pwsh startup
Write-Test "Bare pwsh startup time (no psmux)"
$barePwshTimes = @()
for ($i = 0; $i -lt 3; $i++) {
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $out = pwsh -NoProfile -Command "Write-Output 'READY'" 2>&1 | Out-String
    $sw.Stop()
    if ($out -match "READY") {
        $barePwshTimes += $sw.ElapsedMilliseconds
        Write-Info "  Bare pwsh run $($i+1): $($sw.ElapsedMilliseconds)ms"
    }
}

if ($barePwshTimes.Count -gt 0) {
    $barePwshAvg = [math]::Round(($barePwshTimes | Measure-Object -Average).Average, 1)
    Write-Perf "Bare pwsh startup: avg=${barePwshAvg}ms (n=$($barePwshTimes.Count))"
} else {
    $barePwshAvg = 0
    Write-Skip "Could not measure bare pwsh startup"
}

# 4b: psmux + pwsh (send command to pane and measure until output appears)
Write-Test "psmux pane pwsh readiness"
Reset-Psmux
$sess = "pwsh_perf"
$sw = [System.Diagnostics.Stopwatch]::StartNew()
Start-Process -FilePath $PSMUX -ArgumentList "new-session -s $sess -d" -WindowStyle Hidden
Start-Sleep -Seconds 2

# Send a marker command and measure until it appears in capture-pane
& $PSMUX send-keys -t $sess "echo PSMUX_PERF_MARKER_$(Get-Random)" Enter 2>$null
$marker_sent = [System.Diagnostics.Stopwatch]::StartNew()

$found = $false
for ($j = 0; $j -lt 100; $j++) {
    Start-Sleep -Milliseconds 100
    $capture = & $PSMUX capture-pane -t $sess -p 2>&1 | Out-String
    if ($capture -match "PSMUX_PERF_MARKER") {
        $found = $true
        break
    }
}
$marker_sent.Stop()

if ($found) {
    Write-Perf "psmux pane command echo: $($marker_sent.ElapsedMilliseconds)ms (includes pwsh processing)"
    Write-Pass "pwsh inside psmux responded within $($marker_sent.ElapsedMilliseconds)ms"
} else {
    Write-Fail "pwsh inside psmux did not process command within 10s"
}

# ===========================================================================
# MEASUREMENT 5: TCP command round-trip latency
# ===========================================================================
Write-Host ""
Write-Host ("=" * 70)
Write-Host "  5. TCP COMMAND ROUND-TRIP LATENCY"
Write-Host ("=" * 70)

$port = (Get-Content "$PSMUX_DIR\$sess.port" -ErrorAction SilentlyContinue)
$key = (Get-Content "$PSMUX_DIR\$sess.key" -ErrorAction SilentlyContinue)

if ($port -and $key) {
    $tcpTimes = @()
    for ($i = 0; $i -lt 20; $i++) {
        try {
            $sw = [System.Diagnostics.Stopwatch]::StartNew()
            $client = New-Object System.Net.Sockets.TcpClient("127.0.0.1", [int]$port)
            $stream = $client.GetStream()
            $stream.ReadTimeout = 5000
            $writer = New-Object System.IO.StreamWriter($stream)
            $reader = New-Object System.IO.StreamReader($stream)
            $writer.WriteLine("AUTH $key")
            $writer.Flush()
            $auth = $reader.ReadLine()
            $writer.WriteLine("show-options -g")
            $writer.Flush()
            $resp = $reader.ReadToEnd()
            $client.Close()
            $sw.Stop()
            $tcpTimes += $sw.ElapsedMilliseconds
        } catch {
            # skip
        }
    }

    if ($tcpTimes.Count -gt 0) {
        $sorted = $tcpTimes | Sort-Object
        $avg = [math]::Round(($sorted | Measure-Object -Average).Average, 1)
        $p50 = $sorted[[math]::Floor($sorted.Count * 0.5)]
        $p90 = $sorted[[math]::Floor($sorted.Count * 0.9)]
        $p99 = $sorted[-1]
        $min = $sorted[0]
        Write-Perf "TCP show-options round-trip: avg=${avg}ms  P50=${p50}ms  P90=${p90}ms  P99=${p99}ms  min=${min}ms  (n=$($tcpTimes.Count))"
        if ($avg -lt 50) { Write-Pass "TCP latency under 50ms" }
        elseif ($avg -lt 100) { Write-Pass "TCP latency under 100ms (acceptable)" }
        else { Write-Fail "TCP latency too high: ${avg}ms" }
    } else {
        Write-Fail "No successful TCP measurements"
    }
} else {
    Write-Skip "TCP latency test — port/key not found"
}

# ===========================================================================
# MEASUREMENT 6: dump-state JSON latency
# ===========================================================================
Write-Host ""
Write-Host ("=" * 70)
Write-Host "  6. DUMP-STATE JSON LATENCY"
Write-Host ("=" * 70)

if ($port -and $key) {
    $dumpTimes = @()
    $dumpSizes = @()
    for ($i = 0; $i -lt 10; $i++) {
        try {
            $sw = [System.Diagnostics.Stopwatch]::StartNew()
            $client = New-Object System.Net.Sockets.TcpClient("127.0.0.1", [int]$port)
            $stream = $client.GetStream()
            $stream.ReadTimeout = 5000
            $writer = New-Object System.IO.StreamWriter($stream)
            $reader = New-Object System.IO.StreamReader($stream)
            $writer.WriteLine("AUTH $key")
            $writer.Flush()
            $auth = $reader.ReadLine()
            $writer.WriteLine("dump-state")
            $writer.Flush()
            $resp = $reader.ReadToEnd()
            $client.Close()
            $sw.Stop()
            if ($resp -match '"layout"') {
                $dumpTimes += $sw.ElapsedMilliseconds
                $dumpSizes += $resp.Length
            }
        } catch {}
    }

    if ($dumpTimes.Count -gt 0) {
        $avg = [math]::Round(($dumpTimes | Measure-Object -Average).Average, 1)
        $avgSize = [math]::Round(($dumpSizes | Measure-Object -Average).Average, 0)
        Write-Perf "dump-state: avg=${avg}ms  payload=${avgSize} bytes  (n=$($dumpTimes.Count))"
        if ($avg -lt 50) { Write-Pass "dump-state under 50ms" }
        else { Write-Pass "dump-state: ${avg}ms" }
    }
} else {
    Write-Skip "dump-state test — no port/key"
}

# ===========================================================================
# MEASUREMENT 7: Rapid set-option throughput
# ===========================================================================
Write-Host ""
Write-Host ("=" * 70)
Write-Host "  7. RAPID SET-OPTION THROUGHPUT"
Write-Host ("=" * 70)

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$count = 100
for ($i = 0; $i -lt $count; $i++) {
    & $PSMUX set -g "@perf-test-$i" "value-$i" -t $sess 2>$null
}
$sw.Stop()
$opsPerSec = [math]::Round($count / ($sw.ElapsedMilliseconds / 1000.0), 0)
Write-Perf "set-option: $count ops in $($sw.ElapsedMilliseconds)ms = $opsPerSec ops/sec"
if ($opsPerSec -gt 35) { Write-Pass "set-option throughput > 35 ops/sec ($opsPerSec ops/sec)" }
else { Write-Fail "set-option throughput too low: $opsPerSec ops/sec" }

# ===========================================================================
# COMPARISON TABLE
# ===========================================================================
Write-Host ""
Write-Host ("=" * 76)
Write-Host "                    PERFORMANCE SUMMARY"
Write-Host ("=" * 76)
Write-Host ""
Write-Host ("  {0,-45} {1,12}" -f "Metric", "Value")
Write-Host ("  {0,-45} {1,12}" -f ("-" * 45), ("-" * 12))

if ($startupTimes.Count -gt 0) {
    $v = [math]::Round(($startupTimes | Measure-Object -Average).Average, 0)
    Write-Host ("  {0,-45} {1,10} ms" -f "Server startup (bare, no config)", $v)
}
if ($pluginTimes.Count -gt 0) {
    $v = [math]::Round(($pluginTimes | Measure-Object -Average).Average, 0)
    Write-Host ("  {0,-45} {1,10} ms" -f "Server startup (2 plugins + theme)", $v)
}
if ($startupTimes.Count -gt 0 -and $pluginTimes.Count -gt 0) {
    $overhead = [math]::Round(($pluginTimes | Measure-Object -Average).Average - ($startupTimes | Measure-Object -Average).Average, 0)
    Write-Host ("  {0,-45} {1,10} ms" -f "Plugin auto-source overhead", $overhead)
}
if ($barePwshAvg -gt 0) {
    Write-Host ("  {0,-45} {1,10} ms" -f "Bare pwsh startup (no psmux)", [math]::Round($barePwshAvg, 0))
}
if ($marker_sent) {
    Write-Host ("  {0,-45} {1,10} ms" -f "psmux pane command echo", $marker_sent.ElapsedMilliseconds)
}
if ($tcpTimes.Count -gt 0) {
    Write-Host ("  {0,-45} {1,10} ms" -f "TCP round-trip (show-options)", [math]::Round(($tcpTimes | Measure-Object -Average).Average, 1))
}
if ($dumpTimes.Count -gt 0) {
    Write-Host ("  {0,-45} {1,10} ms" -f "TCP round-trip (dump-state)", [math]::Round(($dumpTimes | Measure-Object -Average).Average, 1))
}
Write-Host ("  {0,-45} {1,10} /s" -f "set-option throughput", $opsPerSec)
Write-Host ""

# Windows Terminal reference baselines (from WT source code)
Write-Host "  Windows Terminal Reference Baselines:"
Write-Host ("  {0,-45} {1,12}" -f ("-" * 45), ("-" * 12))
Write-Host ("  {0,-45} {1,10} ms" -f "WT cold start (from Microsoft docs)", "~800-1200")
Write-Host ("  {0,-45} {1,10} KB" -f "WT ConPTY pipe buffer", "128")
Write-Host ("  {0,-45} {1,10}" -f "WT render loop", "VSync ~60Hz")
Write-Host ("  {0,-45} {1,10}" -f "WT mouse handling", "No throttle")
Write-Host ("  {0,-45} {1,10}" -f "WT architecture", "In-process (no TCP)")
Write-Host ""

# ===========================================================================
# CLEANUP
# ===========================================================================
Reset-Psmux
Remove-Item "$env:TEMP\psmux_perf_*.conf" -Force -ErrorAction SilentlyContinue

Write-Host ("=" * 76)
$total = $script:TestsPassed + $script:TestsFailed + $script:TestsSkipped
Write-Host "  RESULTS: $script:TestsPassed passed, $script:TestsFailed failed, $script:TestsSkipped skipped (of $total)"
Write-Host ("=" * 76)

if ($script:TestsFailed -gt 0) { exit 1 } else { exit 0 }
