#Requires -Version 5.1
<#
.SYNOPSIS
    LLMHosts Customer Experience Test - Alienware (Windows 11)

.DESCRIPTION
    Tests exactly what a customer does:
      1. pip install llmhosts
      2. llmhosts doctor
      3. llmhosts serve
      4. Make API calls
    If anything fails, it's a product bug - not a test gap.

.NOTES
    Only prerequisite: Python 3.10+
    Everything else (Ollama, models, etc.) should be handled by llmhosts.

.EXAMPLE
    .\run-alienware.ps1
    .\run-alienware.ps1 -SkipInstall     # Already installed, just test
    .\run-alienware.ps1 -PostToGitHub     # Auto-post report to issue #359
#>

param(
    [switch]$SkipInstall,
    [switch]$PostToGitHub,
    [int]$Port = 4000
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Continue"

$BASE_URL = "http://127.0.0.1:$Port"
$GITHUB_ISSUE = 359
$serverProcess = $null

# Resolve the llmhosts command - prefer direct exe, fall back to python -m
$LLMHOSTS_CMD = $null
$LLMHOSTS_ARGS_PREFIX = @()
if (Get-Command llmhosts -ErrorAction SilentlyContinue) {
    $LLMHOSTS_CMD = "llmhosts"
} else {
    # pip installed to a Scripts dir not on PATH - use python -m fallback
    $LLMHOSTS_CMD = "python"
    $LLMHOSTS_ARGS_PREFIX = @("-m", "llmhosts")
}

function Invoke-LLMHosts {
    param([Parameter(ValueFromRemainingArguments)]$Args)
    & $LLMHOSTS_CMD @LLMHOSTS_ARGS_PREFIX @Args
}

# ============================================================================
# HELPERS
# ============================================================================

function Write-Step($num, $total, $msg) {
    Write-Host "`n[$num/$total] $msg" -ForegroundColor Cyan
}

function Write-Pass($msg, $detail = "") {
    $out = "  PASS  $msg"
    if ($detail) { $out += " -- $detail" }
    Write-Host $out -ForegroundColor Green
}

function Write-Fail($msg, $detail = "") {
    $out = "  FAIL  $msg"
    if ($detail) { $out += " -- $detail" }
    Write-Host $out -ForegroundColor Red
}

function Write-Info($msg) {
    Write-Host "  $msg" -ForegroundColor DarkGray
}

function Test-Endpoint($url, $method = "GET", $body = $null, $timeout = 120) {
    try {
        $params = @{ Uri = $url; Method = $method; TimeoutSec = $timeout; UseBasicParsing = $true }
        if ($body) { $params.Body = $body; $params.ContentType = "application/json" }
        $start = Get-Date
        $resp = Invoke-WebRequest @params
        $elapsed = [math]::Round(((Get-Date) - $start).TotalMilliseconds)
        return @{ OK = $true; Status = $resp.StatusCode; Body = $resp.Content; Ms = $elapsed }
    } catch {
        return @{ OK = $false; Error = $_.Exception.Message; Ms = 0 }
    }
}

function Wait-ForHealth($url, $maxWait = 30) {
    Write-Info "Waiting for server to be ready..."
    for ($i = 0; $i -lt $maxWait; $i++) {
        $r = Test-Endpoint "$url/health" -timeout 3
        if ($r.OK) { return $true }
        Start-Sleep -Seconds 1
    }
    return $false
}

# ============================================================================
# REPORT
# ============================================================================

$report = [ordered]@{
    timestamp = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")
    machine   = $env:COMPUTERNAME
    os        = [System.Environment]::OSVersion.VersionString
    gpu       = ""
    steps     = [ordered]@{}
    verdict   = "UNKNOWN"
}

# GPU detection (informational only - llmhosts should detect this itself)
try {
    $gpu = (Get-CimInstance Win32_VideoController | Where-Object { $_.Name -match "NVIDIA|AMD|RTX|GTX|Radeon" } | Select-Object -First 1).Name
    if ($gpu) { $report.gpu = $gpu } else { $report.gpu = "none detected" }
} catch { $report.gpu = "detection failed" }

$totalSteps = 6
$failures = 0

Write-Host ""
Write-Host "========================================" -ForegroundColor White
Write-Host "  LLMHosts - Customer Experience Test" -ForegroundColor White
Write-Host "========================================" -ForegroundColor White
Write-Host "  Machine: $($report.machine)" -ForegroundColor DarkGray
Write-Host "  GPU:     $($report.gpu)" -ForegroundColor DarkGray
if ($LLMHOSTS_ARGS_PREFIX.Count -gt 0) {
    Write-Host "  CLI:     python -m llmhosts (not on PATH)" -ForegroundColor Yellow
} else {
    Write-Host "  CLI:     llmhosts" -ForegroundColor DarkGray
}

# ============================================================================
# STEP 1: INSTALL FROM PYPI
# ============================================================================
Write-Step 1 $totalSteps "pip install llmhosts"

if ($SkipInstall) {
    Write-Info "Skipped (--SkipInstall)"
    $report.steps["install"] = @{ passed = $true; skipped = $true }
} else {
    try {
        $installOut = & pip install llmhosts --upgrade 2>&1
        $installOk = $LASTEXITCODE -eq 0
        if ($installOk) {
            # Re-resolve command after install (Scripts dir may now exist)
            if (Get-Command llmhosts -ErrorAction SilentlyContinue) {
                $LLMHOSTS_CMD = "llmhosts"; $LLMHOSTS_ARGS_PREFIX = @()
            }
            $ver = Invoke-LLMHosts --version 2>&1
            Write-Pass "Installed" "$ver"
            $report.steps["install"] = @{ passed = $true; version = "$ver"; output = "$installOut" }
        } else {
            Write-Fail "pip install failed" "$installOut"
            $report.steps["install"] = @{ passed = $false; output = "$installOut" }
            $failures++
        }
    } catch {
        Write-Fail "pip install crashed" "$($_.Exception.Message)"
        $report.steps["install"] = @{ passed = $false; error = "$($_.Exception.Message)" }
        $failures++
    }
}

# ============================================================================
# STEP 2: DOCTOR
# ============================================================================
$cmdLabel = if ($LLMHOSTS_ARGS_PREFIX.Count -gt 0) { "python -m llmhosts" } else { "llmhosts" }
Write-Step 2 $totalSteps "$cmdLabel doctor"

try {
    $doctorOut = Invoke-LLMHosts doctor 2>&1
    $doctorOk = $LASTEXITCODE -eq 0

    # Show doctor output directly - it's the product's own diagnostics
    Write-Host ""
    $doctorOut | ForEach-Object { Write-Host "  $_" -ForegroundColor Gray }
    Write-Host ""

    if ($doctorOk) {
        Write-Pass "Doctor passed"
    } else {
        Write-Pass 'Doctor ran (non-zero exit is OK - it reports issues to fix)'
    }
    $report.steps["doctor"] = @{ passed = $true; output = "$doctorOut" }
} catch {
    Write-Fail "llmhosts doctor not found" "Is llmhosts installed?"
    $report.steps["doctor"] = @{ passed = $false; error = "$($_.Exception.Message)" }
    $failures++
}

# ============================================================================
# STEP 3: START SERVER
# ============================================================================
Write-Step 3 $totalSteps "$cmdLabel serve --no-tui"

# Check if already running
$alreadyRunning = (Test-Endpoint "$BASE_URL/health" -timeout 3).OK

if ($alreadyRunning) {
    Write-Pass "Server already running on port $Port"
    $report.steps["serve"] = @{ passed = $true; note = "already running" }
} else {
    Write-Info "Starting server in background..."
    try {
        $serveArgs = @($LLMHOSTS_ARGS_PREFIX) + @("serve", "--no-tui", "--port", $Port)
        $serverProcess = Start-Process -FilePath $LLMHOSTS_CMD -ArgumentList $serveArgs `
            -PassThru -WindowStyle Hidden -RedirectStandardOutput "$env:TEMP\llmhosts-stdout.log" `
            -RedirectStandardError "$env:TEMP\llmhosts-stderr.log"

        $healthy = Wait-ForHealth $BASE_URL 45

        if ($healthy) {
            Write-Pass "Server started" "port $Port, PID $($serverProcess.Id)"
            $report.steps["serve"] = @{ passed = $true; pid = $serverProcess.Id }
        } else {
            # Show what went wrong
            $stderr = ""
            if (Test-Path "$env:TEMP\llmhosts-stderr.log") {
                $stderr = Get-Content "$env:TEMP\llmhosts-stderr.log" -Tail 20 -ErrorAction SilentlyContinue | Out-String
            }
            Write-Fail "Server did not become healthy in 45s"
            if ($stderr) { Write-Info "stderr: $stderr" }
            $report.steps["serve"] = @{ passed = $false; error = "timeout"; stderr = "$stderr" }
            $failures++
        }
    } catch {
        Write-Fail "Could not start server" "$($_.Exception.Message)"
        $report.steps["serve"] = @{ passed = $false; error = "$($_.Exception.Message)" }
        $failures++
    }
}

# ============================================================================
# STEP 4: HEALTH + MODELS (the first thing a customer's SDK hits)
# ============================================================================
Write-Step 4 $totalSteps "API health + model discovery"

$health = Test-Endpoint "$BASE_URL/health"
if ($health.OK) {
    $healthData = $health.Body | ConvertFrom-Json
    $version = $healthData.version
    Write-Pass "/health" "${version} ($($health.Ms)ms)"
    $report.steps["health"] = @{ passed = $true; version = "$version"; latencyMs = $health.Ms }
} else {
    Write-Fail "/health" "$($health.Error)"
    $report.steps["health"] = @{ passed = $false; error = "$($health.Error)" }
    $failures++
}

$models = Test-Endpoint "$BASE_URL/v1/models"
$modelList = @()
if ($models.OK) {
    $modelsData = $models.Body | ConvertFrom-Json
    $modelList = @($modelsData.data | ForEach-Object { $_.id })
    Write-Pass "/v1/models" "$($modelList.Count) models: $($modelList -join ', ')"
    $report.steps["models"] = @{ passed = $true; count = $modelList.Count; models = $modelList }
} else {
    Write-Fail "/v1/models" "$($models.Error)"
    $report.steps["models"] = @{ passed = $false; error = "$($models.Error)" }
    $failures++
}

# ============================================================================
# STEP 5: INFERENCE (the whole point)
# ============================================================================
Write-Step 5 $totalSteps "Inference - make a real API call"

if ($modelList.Count -gt 0) {
    $testModel = $modelList[0]
    Write-Info "Using model: $testModel"

    $body = @{
        model = $testModel
        messages = @(@{ role = "user"; content = "Say hello in exactly 5 words." })
        max_tokens = 50
    } | ConvertTo-Json -Depth 3

    $infer = Test-Endpoint "$BASE_URL/v1/chat/completions" "POST" $body 120
    if ($infer.OK) {
        $inferData = $infer.Body | ConvertFrom-Json
        $response = $inferData.choices[0].message.content
        Write-Pass "Inference" ("" + $infer.Ms + "ms - `"" + $response + "`"")
        $report.steps["inference"] = @{ passed = $true; model = $testModel; latencyMs = $infer.Ms; response = "$response" }
    } else {
        Write-Fail "Inference" "$($infer.Error)"
        $report.steps["inference"] = @{ passed = $false; model = $testModel; error = "$($infer.Error)" }
        $failures++
    }
} else {
    Write-Fail "Inference" 'No models available - nothing to test'
    $report.steps["inference"] = @{ passed = $false; error = "no models" }
    $failures++
}

# ============================================================================
# STEP 6: THROUGHPUT (5 sequential - proves stability)
# ============================================================================
Write-Step 6 $totalSteps "Throughput - 5 sequential requests"

if ($modelList.Count -gt 0) {
    $testModel = $modelList[0]
    $latencies = @()
    $ok = 0

    for ($i = 1; $i -le 5; $i++) {
        $body = @{
            model = $testModel
            messages = @(@{ role = "user"; content = "Count to $i." })
            max_tokens = 20
        } | ConvertTo-Json -Depth 3
        $r = Test-Endpoint "$BASE_URL/v1/chat/completions" "POST" $body 120
        if ($r.OK) { $latencies += $r.Ms; $ok++ }
    }

    if ($ok -eq 5) {
        $avg = [math]::Round(($latencies | Measure-Object -Average).Average)
        $min = ($latencies | Measure-Object -Minimum).Minimum
        $max = ($latencies | Measure-Object -Maximum).Maximum
        Write-Pass "5/5 passed" "avg ${avg}ms (${min}-${max}ms)"
        $report.steps["throughput"] = @{ passed = $true; latencies = $latencies; avgMs = $avg }
    } else {
        Write-Fail "$ok/5 passed"
        $report.steps["throughput"] = @{ passed = $false; successful = $ok }
        $failures++
    }
} else {
    Write-Fail "Throughput" 'No models - skipped'
    $report.steps["throughput"] = @{ passed = $false; error = "no models" }
    $failures++
}

# ============================================================================
# CLEANUP
# ============================================================================

if ($serverProcess -and -not $serverProcess.HasExited) {
    Write-Info "Stopping server (PID $($serverProcess.Id))..."
    Stop-Process -Id $serverProcess.Id -Force -ErrorAction SilentlyContinue
}

# ============================================================================
# VERDICT
# ============================================================================

Write-Host ""
Write-Host "========================================" -ForegroundColor White

if ($failures -eq 0) {
    $report.verdict = "PASS"
    Write-Host "  VERDICT: ALL PASSED" -ForegroundColor Green
    Write-Host "  pip install llmhosts && llmhosts serve -- it works." -ForegroundColor Green
} else {
    $report.verdict = "FAIL ($failures issues)"
    Write-Host "  VERDICT: $failures ISSUE(S) FOUND" -ForegroundColor Red
    Write-Host "  These need to be fixed before a customer hits them." -ForegroundColor Yellow
}

Write-Host "========================================" -ForegroundColor White

# Save report
$reportFile = Join-Path $PSScriptRoot "report-$(Get-Date -Format 'yyyy-MM-dd-HHmmss').json"
$reportJson = $report | ConvertTo-Json -Depth 5
$reportJson | Out-File -FilePath $reportFile -Encoding utf8
Write-Host "  Report: $reportFile" -ForegroundColor DarkGray

# ============================================================================
# POST TO GITHUB (optional)
# ============================================================================

if ($PostToGitHub) {
    $stepCount = ($report.steps.Keys | Measure-Object).Count
    $passCount = ($report.steps.Values | Where-Object { $_.passed }).Count

    $md = @"
## Customer Experience Test -- Alienware

**Date**: $(Get-Date -Format "yyyy-MM-dd HH:mm UTC")
**Machine**: $($report.machine) | **GPU**: $($report.gpu)
**Verdict**: **$($report.verdict)**

### Results: $passCount/$stepCount passed

| Step | Result | Details |
|------|--------|---------|
"@

    foreach ($key in $report.steps.Keys) {
        $s = $report.steps[$key]
        $icon = if ($s.passed) { ":white_check_mark:" } else { ":x:" }
        $detail = ""
        if ($s.version) { $detail = $s.version }
        elseif ($s.latencyMs) { $detail = "$($s.latencyMs)ms" }
        elseif ($s.count) { $detail = "$($s.count) items" }
        elseif ($s.response) { $detail = $s.response.Substring(0, [math]::Min(60, $s.response.Length)) }
        elseif ($s.error) { $detail = $s.error }
        elseif ($s.skipped) { $detail = "skipped" }
        $md += "`n| $key | $icon | $detail |"
    }

    $md += "`n`n---`n*Customer Experience Test -- ``run-alienware.ps1``*"

    $tmpFile = [System.IO.Path]::GetTempFileName()
    $md | Out-File -FilePath $tmpFile -Encoding utf8

    try {
        & gh issue comment $GITHUB_ISSUE --body-file $tmpFile
        Write-Host "  Posted to GitHub issue #$GITHUB_ISSUE" -ForegroundColor Green
    } catch {
        Write-Host "  GitHub post failed: $($_.Exception.Message)" -ForegroundColor Yellow
    } finally {
        Remove-Item $tmpFile -ErrorAction SilentlyContinue
    }
}

Write-Host ""
