param(
  [string]$ZipPath,
  [string]$Target = "ubuntu@82.156.24.101",
  [string]$RemoteRoot = "/opt/kdl-olp-demo/8095",
  [string]$BaseUrl = "https://82.156.24.101:8095",
  [switch]$Sudo,
  [switch]$NoSudo,
  [switch]$SkipSmoke
)

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

$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..\..")
$DeployRoot = Join-Path $RepoRoot "kdl-wasm\web\test-results\deploy"

if ([string]::IsNullOrWhiteSpace($ZipPath)) {
  $ZipPath = Get-ChildItem -LiteralPath $DeployRoot -Filter "kdl-olp-demo-*.zip" |
    Sort-Object LastWriteTime -Descending |
    Select-Object -First 1 -ExpandProperty FullName
}

if ([string]::IsNullOrWhiteSpace($ZipPath) -or -not (Test-Path -LiteralPath $ZipPath)) {
  throw "Demo zip not found. Run: npm run demo:build"
}

$ZipPath = (Resolve-Path -LiteralPath $ZipPath).Path
$ZipName = Split-Path -Leaf $ZipPath
if ($ZipName -notmatch '^kdl-olp-demo-(.+)\.zip$') {
  throw "Zip name must match kdl-olp-demo-<release_id>.zip: $ZipName"
}
$ReleaseId = $Matches[1]
$RemoteZip = "/tmp/$ZipName"
$RemoteRelease = "$RemoteRoot/releases/$ReleaseId"

function Invoke-Remote {
  param([string]$Command)
  ssh -o StrictHostKeyChecking=accept-new $Target $Command
  if ($LASTEXITCODE -ne 0) {
    throw "Remote command failed: $Command"
  }
}

function Remote-Cmd {
  param([string]$Command)
  if ($script:UseSudo) {
    return "sudo sh -lc " + (Quote-Sh $Command)
  }
  return "sh -lc " + (Quote-Sh $Command)
}

function Quote-Sh {
  param([string]$Value)
  return "'" + ($Value -replace "'", "'\''") + "'"
}

function Get-RemoteParent {
  param([string]$Path)
  $Normalized = $Path.TrimEnd("/")
  $Index = $Normalized.LastIndexOf("/")
  if ($Index -le 0) {
    return "/"
  }
  return $Normalized.Substring(0, $Index)
}

$RemoteParent = Get-RemoteParent $RemoteRoot

Write-Host "Deploying $ZipName to ${Target}:$RemoteRoot"

scp -o StrictHostKeyChecking=accept-new $ZipPath "${Target}:$RemoteZip"
if ($LASTEXITCODE -ne 0) {
  throw "scp upload failed"
}

$script:UseSudo = $false
if ($Sudo) {
  $script:UseSudo = $true
} elseif (-not $NoSudo) {
  ssh -o StrictHostKeyChecking=accept-new $Target "test -w $(Quote-Sh $RemoteParent)"
  if ($LASTEXITCODE -ne 0) {
    ssh -o StrictHostKeyChecking=accept-new $Target "sudo -n true"
    if ($LASTEXITCODE -eq 0) {
      $script:UseSudo = $true
    } else {
      throw "Remote root is not writable and passwordless sudo is unavailable. Re-run with a writable -RemoteRoot or configure sudo."
    }
  }
}

$prepare = @(
  "set -eu"
  "mkdir -p $(Quote-Sh "$RemoteRoot/releases")"
  "rm -rf $(Quote-Sh $RemoteRelease)"
  "mkdir -p $(Quote-Sh $RemoteRelease)"
  "unzip -q -o $(Quote-Sh $RemoteZip) -d $(Quote-Sh $RemoteRelease)"
  "test -f $(Quote-Sh "$RemoteRelease/index.html")"
  "test -f $(Quote-Sh "$RemoteRelease/demo-manifest.json")"
  "test -f $(Quote-Sh "$RemoteRelease/app/virtual-controller.html")"
  "if [ -L $(Quote-Sh "$RemoteRoot/current") ] || [ -e $(Quote-Sh "$RemoteRoot/current") ]; then rm -f $(Quote-Sh "$RemoteRoot/previous"); ln -sfn `"`$(readlink -f $(Quote-Sh "$RemoteRoot/current"))`" $(Quote-Sh "$RemoteRoot/previous"); fi"
  "ln -sfn $(Quote-Sh "$RemoteRelease") $(Quote-Sh "$RemoteRoot/current")"
  "rm -f $(Quote-Sh $RemoteZip)"
) -join "; "

Invoke-Remote (Remote-Cmd $prepare)

$restart = "if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files kdl-site-8095.service >/dev/null 2>&1; then systemctl restart kdl-site-8095; fi"
Invoke-Remote (Remote-Cmd $restart)

$smoke = @{
  release_id = $ReleaseId
  zip = $ZipPath
  target = $Target
  remote_root = $RemoteRoot
  remote_release = $RemoteRelease
  base_url = $BaseUrl
  deployed_at = (Get-Date).ToUniversalTime().ToString("o")
  urls = @()
}

if (-not $SkipSmoke) {
  $nodeSmoke = @"
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
const baseUrl = "$BaseUrl";
const get = async (path) => {
  const response = await fetch(baseUrl + path);
  const buffer = Buffer.from(await response.arrayBuffer());
  if (response.status !== 200) {
    throw new Error(path + " returned " + response.status);
  }
  return {
    path,
    status: response.status,
    bytes: buffer.length,
    text: buffer.toString("utf8")
  };
};
const manifestResponse = await get("/demo-manifest.json");
const manifest = JSON.parse(manifestResponse.text);
const jobId = manifest.job_id;
const paths = [
  "/",
  "/demo-manifest.json",
  "/spec-programs/manifest.json",
  "/app/virtual-controller.html",
  "/test-results/abb120-spec/" + jobId + "/report.json",
  "/test-results/abb120-spec/" + jobId + "/report.html",
  "/test-results/virtual-controller/virtual-controller-desktop.png",
  "/test-results/virtual-controller/virtual-controller-mobile.png"
];
const urls = [];
for (const path of paths) {
  const result = path === "/demo-manifest.json" ? manifestResponse : await get(path);
  urls.push({ url: baseUrl + path, status: result.status, bytes: result.bytes });
}
const reportResponse = await get("/test-results/abb120-spec/" + jobId + "/report.json");
const report = JSON.parse(reportResponse.text);
if (Number(manifest.program_count) !== 19) {
  throw new Error("Expected program_count=19, got " + manifest.program_count);
}
if (Number(report.summary.fail) !== 0) {
  throw new Error("Expected summary.fail=0, got " + report.summary.fail);
}
console.log(JSON.stringify({
  job_id: jobId,
  program_count: Number(manifest.program_count),
  summary_fail: Number(report.summary.fail),
  urls
}));
"@
  $nodeOutput = $nodeSmoke | node --input-type=module
  if ($LASTEXITCODE -ne 0) {
    throw "Smoke check failed"
  }
  $nodeSmokeResult = $nodeOutput | Select-Object -Last 1 | ConvertFrom-Json
  $smoke.job_id = $nodeSmokeResult.job_id
  $smoke.program_count = [int]$nodeSmokeResult.program_count
  $smoke.summary_fail = [int]$nodeSmokeResult.summary_fail
  $smoke.urls = @($nodeSmokeResult.urls)
}

$SmokePath = Join-Path $DeployRoot "kdl-olp-demo-$ReleaseId.deploy.json"
$smoke | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $SmokePath -Encoding UTF8
$smoke | ConvertTo-Json -Depth 8
