param(
  [int]$BrowserPort = 0
)

$ErrorActionPreference = 'Stop'

$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..\..')).Path
$frontendRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$tempCacheRoot = Join-Path $env:TEMP 'ums-e2e-cache'
$goCacheDir = Join-Path $tempCacheRoot 'go-build'
$goModCacheDir = Join-Path $tempCacheRoot 'gomod'
$goPathDir = Join-Path $tempCacheRoot 'gopath'
$serverExePath = Join-Path $env:TEMP ("ums-server-smoke-" + [guid]::NewGuid().ToString('N') + '.exe')

New-Item -ItemType Directory -Force $goCacheDir, $goModCacheDir, $goPathDir | Out-Null

function Test-UrlReady {
  param(
    [Parameter(Mandatory = $true)][string]$Url
  )

  try {
    $response = Invoke-WebRequest $Url -UseBasicParsing -TimeoutSec 2
    return $response.StatusCode -ge 200 -and $response.StatusCode -lt 500
  } catch {
    return $false
  }
}

function Wait-UrlReady {
  param(
    [Parameter(Mandatory = $true)][string]$Url,
    [Parameter(Mandatory = $true)][string]$Label,
    [int]$RetryCount = 120,
    [int]$DelayMs = 500
  )

  for ($i = 0; $i -lt $RetryCount; $i++) {
    if (Test-UrlReady -Url $Url) {
      return
    }
    Start-Sleep -Milliseconds $DelayMs
  }

  throw "$Label did not become ready: $Url"
}

function Start-ManagedProcess {
  param(
    [Parameter(Mandatory = $true)][string]$Name,
    [Parameter(Mandatory = $true)][string]$FilePath,
    [string[]]$ArgumentList = @(),
    [Parameter(Mandatory = $true)][string]$WorkingDirectory
  )

  $stdoutPath = Join-Path $env:TEMP "$Name-stdout.log"
  $stderrPath = Join-Path $env:TEMP "$Name-stderr.log"
  Remove-Item $stdoutPath, $stderrPath -Force -ErrorAction SilentlyContinue

  if ($ArgumentList -and $ArgumentList.Count -gt 0) {
    $process = Start-Process `
      -FilePath $FilePath `
      -ArgumentList $ArgumentList `
      -WorkingDirectory $WorkingDirectory `
      -PassThru `
      -WindowStyle Hidden `
      -RedirectStandardOutput $stdoutPath `
      -RedirectStandardError $stderrPath
  } else {
    $process = Start-Process `
      -FilePath $FilePath `
      -WorkingDirectory $WorkingDirectory `
      -PassThru `
      -WindowStyle Hidden `
      -RedirectStandardOutput $stdoutPath `
      -RedirectStandardError $stderrPath
  }

  return [pscustomobject]@{
    Name = $Name
    Process = $process
    StdOut = $stdoutPath
    StdErr = $stderrPath
  }
}

function Stop-ManagedProcess {
  param(
    [Parameter(Mandatory = $false)]$Handle
  )

  if (-not $Handle) {
    return
  }

  if ($Handle.Process -and -not $Handle.Process.HasExited) {
    try {
      taskkill /PID $Handle.Process.Id /T /F *> $null
    } catch {
      Stop-Process -Id $Handle.Process.Id -Force -ErrorAction SilentlyContinue
    }
  }
}

function Show-ManagedProcessLogs {
  param(
    [Parameter(Mandatory = $false)]$Handle
  )

  if (-not $Handle) {
    return
  }

  if (Test-Path $Handle.StdOut) {
    Get-Content $Handle.StdOut -ErrorAction SilentlyContinue
  }
  if (Test-Path $Handle.StdErr) {
    Get-Content $Handle.StdErr -ErrorAction SilentlyContinue
  }
}

function Remove-ManagedProcessLogs {
  param(
    [Parameter(Mandatory = $false)]$Handle
  )

  if (-not $Handle) {
    return
  }

  Remove-Item $Handle.StdOut, $Handle.StdErr -Force -ErrorAction SilentlyContinue
}

$backendHandle = $null
$frontendHandle = $null
$startedBackend = $false
$startedFrontend = $false

try {
  Push-Location $projectRoot
  try {
    $env:GOCACHE = $goCacheDir
    $env:GOMODCACHE = $goModCacheDir
    $env:GOPATH = $goPathDir
    go build -o $serverExePath .\cmd\server\main.go
    if ($LASTEXITCODE -ne 0) {
      throw 'server build failed'
    }
  } finally {
    Pop-Location
    Remove-Item Env:GOCACHE -ErrorAction SilentlyContinue
    Remove-Item Env:GOMODCACHE -ErrorAction SilentlyContinue
    Remove-Item Env:GOPATH -ErrorAction SilentlyContinue
  }

  if (-not (Test-UrlReady -Url 'http://127.0.0.1:8080/health')) {
    $backendHandle = Start-ManagedProcess `
      -Name 'ums-backend-smoke' `
      -FilePath $serverExePath `
      -WorkingDirectory $projectRoot
    $startedBackend = $true

    try {
      Wait-UrlReady -Url 'http://127.0.0.1:8080/health' -Label 'backend smoke'
    } catch {
      Show-ManagedProcessLogs $backendHandle
      throw
    }
  }

  if (-not (Test-UrlReady -Url 'http://127.0.0.1:3000')) {
    $frontendHandle = Start-ManagedProcess `
      -Name 'ums-frontend-smoke' `
      -FilePath 'npm.cmd' `
      -ArgumentList @('run', 'dev', '--', '--host', '127.0.0.1', '--port', '3000') `
      -WorkingDirectory $frontendRoot
    $startedFrontend = $true

    try {
      Wait-UrlReady -Url 'http://127.0.0.1:3000' -Label 'frontend smoke'
    } catch {
      Show-ManagedProcessLogs $frontendHandle
      throw
    }
  }

  Push-Location $frontendRoot
  try {
    & (Join-Path $PSScriptRoot 'run-cdp-smoke.ps1') -Port $BrowserPort
  } finally {
    Pop-Location
  }
} finally {
  if ($startedFrontend) {
    Stop-ManagedProcess $frontendHandle
    Remove-ManagedProcessLogs $frontendHandle
  }

  if ($startedBackend) {
    Stop-ManagedProcess $backendHandle
    Remove-ManagedProcessLogs $backendHandle
  }

  Remove-Item $serverExePath -Force -ErrorAction SilentlyContinue
}
