function Build-WindowsPayload {
  param(
    [Parameter(Mandatory = $true)]
    [string]$Root,
    [Parameter(Mandatory = $true)]
    [string]$PayloadDir,
    [string]$Version = "2.0.0",
    [string]$Arch = "win-x64",
    [string]$NodeZipUrl = "https://nodejs.org/dist/v20.19.5/node-v20.19.5-win-x64.zip",
    [string]$NodeVersion = "20.19.5"
  )

  $ErrorActionPreference = "Stop"

  $AppDir = Join-Path $PayloadDir "app"
  $RuntimeDir = Join-Path $PayloadDir "runtime"

  Write-Host "Building payload v$Version ($Arch) -> $PayloadDir"

  if (Test-Path $PayloadDir) {
    Remove-Item $PayloadDir -Recurse -Force
  }
  New-Item -ItemType Directory -Path $AppDir -Force | Out-Null
  New-Item -ItemType Directory -Path $RuntimeDir -Force | Out-Null

  $exclude = @("node_modules", ".git", "dist", "test", "desktop")
  Get-ChildItem $Root | Where-Object { $exclude -notcontains $_.Name } | ForEach-Object {
    Copy-Item $_.FullName -Destination $AppDir -Recurse -Force
  }

  Push-Location $AppDir
  try {
    npm ci --omit=dev
  } finally {
    Pop-Location
  }

  $zip = Join-Path $env:TEMP "siraya-node-$Arch.zip"
  $extractDir = Join-Path $env:TEMP "siraya-node-$Arch"
  if (Test-Path $extractDir) {
    Remove-Item $extractDir -Recurse -Force
  }

  Invoke-WebRequest -Uri $NodeZipUrl -OutFile $zip
  Expand-Archive -Path $zip -DestinationPath $extractDir -Force

  $nodeFolder = Get-ChildItem $extractDir -Directory | Select-Object -First 1
  if (-not $nodeFolder) {
    throw "Node zip did not contain a folder: $extractDir"
  }
  Copy-Item (Join-Path $nodeFolder.FullName "node.exe") (Join-Path $RuntimeDir "node.exe")

  Remove-Item $zip -Force -ErrorAction SilentlyContinue

  @{
    version     = $Version
    arch        = $Arch
    nodeVersion = $NodeVersion
    builtAt     = (Get-Date).ToUniversalTime().ToString("o")
    gitSha      = $(if ($env:GITHUB_SHA) { $env:GITHUB_SHA } else { "" })
  } | ConvertTo-Json | Set-Content (Join-Path $PayloadDir "payload-manifest.json") -Encoding UTF8

  Write-Host "Payload ready: app=$AppDir runtime=$RuntimeDir"
}
