#Requires -Version 5.1
<#
  Ormur — Test connection (FAZ4 zero-config PC installer).

  Runs AFTER install/pair to give the buyer a pre-payment confidence check: is the mesh
  actually healthy end-to-end? Probes each layer of the local stack + the control-plane/DERP
  relay and prints a green/red report. Safe to re-run any time.

    powershell -ExecutionPolicy Bypass -File test-connection.ps1

  Checks (mirrors the runtime the phone talks to):
    1. sessiond  127.0.0.1:8099   durable session host (owns the terminals)
    2. serve     127.0.0.1:8088   browser/proxy frontend  -> GET /health == {"ok":true}
    3. tsnet     127.0.0.1:7766   tailnet frontend single-instance guard (proves it is up)
    4. paired    machine has completed pairing (state on disk)
    5. relay     control-plane / DERP host reachable over the internet (GET /health)
    6. mesh IP   `agent ip` brings the node up keyless and returns a 100.x tailnet IP
                 (this exercises the real netcheck/DERP path end-to-end)

  Exit code 0 = all core checks green, 1 = one or more red.
#>
[CmdletBinding()]
param(
  [string]$InstallDir = "$env:ProgramFiles\Ormur",
  [string]$Broker     = 'https://46-225-25-222.sslip.io',   # embedded default control-plane / DERP host
  [switch]$Quiet
)
$ErrorActionPreference = 'Stop'

# --- machine-wide state pins (identical to run_*.cmd / repair.cmd) so `agent` sees the paired node ---
$State = Join-Path $env:ProgramData 'Ormur\state'
$env:ARGUS_BETA_PAIRING_DIR = $State
$env:ARGUS_BETA_TSNET_DIR    = Join-Path $State 'tsnet'
$env:ARGUS_BETA_AGENT_KEY    = Join-Path $State 'agent_ed25519'
$Agent = Join-Path $InstallDir 'agent.exe'

$results = @()
function Add-Result([string]$name, [bool]$ok, [string]$detail, [bool]$core = $true) {
  $script:results += [pscustomobject]@{ Name=$name; Ok=$ok; Detail=$detail; Core=$core }
}

# ---- 1-4: local stack via `agent status` (one shot: sessiond/serve/tsnet-guard/paired) ----
$statusOut = ''
try { if (Test-Path $Agent) { $statusOut = (& $Agent status 2>&1 | Out-String) } } catch { $statusOut = "$_" }

function Test-Port([string]$label, [string]$pattern) {
  if ($statusOut -match $pattern) { return ($Matches[1] -eq 'up') }
  # fallback: direct TCP probe if status output was unavailable
  $port = @{ 'sessiond'=8099; 'serve'=8088; 'tsnet'=7766 }[$label]
  if ($port) {
    try { $c = New-Object Net.Sockets.TcpClient; $c.Connect('127.0.0.1',$port); $c.Close(); return $true } catch { return $false }
  }
  return $false
}

$sess  = Test-Port 'sessiond' 'sessiond\(8099\)=(\w+)'
$srv   = Test-Port 'serve'    'serve\(8088\)=(\w+)'
$tsn   = Test-Port 'tsnet'    'tsnet-guard\(7766\)=(\w+)'
$paired = ($statusOut -match 'paired=yes') -or (Test-Path (Join-Path $State 'pairing.json'))

Add-Result 'sessiond (durable session host, :8099)' $sess ($(if($sess){'listening'}else{'not listening'}))
Add-Result 'serve (browser proxy, :8088)'           $srv  ($(if($srv){'listening'}else{'not listening'}))

# 2b: serve health body must be {"ok":true}
$healthOk = $false; $healthDetail = 'no response'
if ($srv) {
  try {
    $h = Invoke-WebRequest -Uri 'http://127.0.0.1:8088/health' -UseBasicParsing -TimeoutSec 5
    $healthOk = ($h.StatusCode -eq 200) -and ($h.Content -match '"ok"\s*:\s*true')
    $healthDetail = "HTTP $($h.StatusCode) $($h.Content.Trim())"
  } catch { $healthDetail = "$_" }
}
Add-Result 'serve /health returns {"ok":true}' $healthOk $healthDetail

Add-Result 'tsnet frontend (:7766 guard)' $tsn ($(if($tsn){'running'}else{'not running'}))
Add-Result 'paired with a phone tenant'   $paired ($(if($paired){'yes'}else{'no — run repair.cmd'}))

# ---- 5: control-plane / DERP relay reachable over the internet ----
# derive the control host from pairing state when present (buyer's real tenant), fall back to $Broker.
$relayHost = $Broker
if ($statusOut -match 'control=(\S+)') {
  $ctl = $Matches[1]
  if ($ctl -match '^https?://') { try { $relayHost = ([Uri]$ctl).GetLeftPart([UriPartial]::Authority) } catch {} }
}
$relayOk = $false; $relayDetail = 'unreachable'
foreach ($base in @($relayHost, $Broker | Select-Object -Unique)) {
  try {
    # any HTTP response (even 404) proves the relay host is reachable through this network.
    $r = Invoke-WebRequest -Uri "$base/health" -UseBasicParsing -TimeoutSec 8
    $relayOk = $true; $relayDetail = "$base -> HTTP $($r.StatusCode)"; break
  } catch {
    if ($_.Exception.Response) { $relayOk = $true; $relayDetail = "$base -> reachable ($($_.Exception.Response.StatusCode.value__))"; break }
    $relayDetail = "$base -> $($_.Exception.Message)"
  }
}
Add-Result 'control-plane / DERP relay reachable' $relayOk $relayDetail

# ---- 6: mesh node comes up + returns a tailnet IP (real DERP/netcheck path) ----
$meshOk = $false; $meshDetail = 'skipped'
if ($paired -and (Test-Path $Agent)) {
  try {
    $ip = ''
    $job = Start-Job -ScriptBlock {
      param($a,$pd,$td,$ak)
      $env:ARGUS_BETA_PAIRING_DIR=$pd; $env:ARGUS_BETA_TSNET_DIR=$td; $env:ARGUS_BETA_AGENT_KEY=$ak
      (& $a ip 2>$null | Select-Object -Last 1)
    } -ArgumentList $Agent,$State,(Join-Path $State 'tsnet'),(Join-Path $State 'agent_ed25519')
    if (Wait-Job $job -Timeout 45) { $ip = (Receive-Job $job | Out-String).Trim() }
    Remove-Job $job -Force -ErrorAction SilentlyContinue
    if ($ip -match '(\d{1,3}(\.\d{1,3}){3})') { $meshOk = $true; $meshDetail = "tailnet IP $($Matches[1])" }
    else { $meshDetail = 'node did not report an IP within 45s' }
  } catch { $meshDetail = "$_" }
} elseif (-not $paired) { $meshDetail = 'not paired yet' }
Add-Result 'on the mesh (agent reports tailnet IP)' $meshOk $meshDetail $false  # bonus, not gating

# ---------------- report ----------------
$green = [char]0x2714; $red = [char]0x2716
function Line([string]$s,[string]$c){ if($Quiet){return}; $o=$Host.UI.RawUI.ForegroundColor; try{$Host.UI.RawUI.ForegroundColor=$c}catch{}; Write-Host $s; try{$Host.UI.RawUI.ForegroundColor=$o}catch{} }

if (-not $Quiet) {
  Write-Host ''
  Write-Host '  Ormur — connection test' -ForegroundColor White
  Write-Host '  ------------------------------------------------------------'
  foreach ($r in $results) {
    $mark = if ($r.Ok) { "  [ OK ]" } else { "  [FAIL]" }
    $col  = if ($r.Ok) { 'Green' } else { 'Red' }
    Line ("{0}  {1}" -f $mark, $r.Name) $col
    if ($r.Detail) { Write-Host ("           {0}" -f $r.Detail) -ForegroundColor DarkGray }
  }
  Write-Host '  ------------------------------------------------------------'
}

$coreFail = @($results | Where-Object { $_.Core -and -not $_.Ok })
if ($coreFail.Count -eq 0) {
  if (-not $Quiet) {
    Write-Host '  ALL CLEAR — your PC is reachable from your phone.' -ForegroundColor Green
    if (-not ($results | Where-Object { $_.Name -like 'on the mesh*' }).Ok) {
      Write-Host '  (mesh IP probe was inconclusive — usually just a slow first netcheck; retry in a minute.)' -ForegroundColor DarkYellow
    }
  }
  exit 0
} else {
  if (-not $Quiet) {
    Write-Host '  SOME CHECKS FAILED.' -ForegroundColor Red
    Write-Host '  Fix hints:' -ForegroundColor Yellow
    if (-not $sess -or -not $srv -or -not $tsn) {
      Write-Host '   - Stack not fully up: it may still be starting. Wait ~20s and re-run this test,' -ForegroundColor Gray
      Write-Host '     or start it now:  wscript "%ProgramFiles%\Ormur\ormur-startup.vbs"' -ForegroundColor Gray
    }
    if (-not $paired) { Write-Host '   - Not paired: double-click %ProgramFiles%\Ormur\repair.cmd to get a fresh code.' -ForegroundColor Gray }
    if (-not $relayOk) { Write-Host '   - Relay unreachable: check this PC''s internet / firewall (outbound HTTPS 443).' -ForegroundColor Gray }
  }
  exit 1
}
