@echo off
setlocal EnableExtensions
title Haraka Support Setup
echo.
echo  Haraka Support - one-click Windows installer
echo  ===========================================
echo  This single file will:
echo    - install Node.js if needed
echo    - install agent + display + mouse/keyboard support
echo    - write config to http://162.0.225.39:8443
echo    - enable auto-start and start the agent
echo  Server: http://162.0.225.39:8443
echo.
echo Running - please wait (may take 1-3 minutes)...
powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference='Stop'; $p='%~f0'; $lines=Get-Content -LiteralPath $p; $i=0; for (; $i -lt $lines.Count; $i++) { if ($lines[$i] -eq ':::PAYLOAD:::') { break } }; if ($i -ge $lines.Count) { throw 'Installer payload missing' }; $script=($lines[($i+1)..($lines.Count-1)]) -join [Environment]::NewLine; $f=Join-Path $env:TEMP ('haraka-setup-' + [guid]::NewGuid().ToString() + '.ps1'); Set-Content -Path $f -Value $script -Encoding UTF8; try { & $f; exit $LASTEXITCODE } finally { Remove-Item -Force $f -ErrorAction SilentlyContinue }"
set ERR=%ERRORLEVEL%
echo.
if %ERR% equ 0 (
  echo SUCCESS. Open http://162.0.225.39:8443 - agent should show Online. Click Connect.
  echo Mouse and keyboard control are included.
) else (
  echo Install failed with code %ERR%.
  echo Re-download from http://162.0.225.39:8443/downloads and try again.
)
echo.
if /I not "%HARAKA_SETUP_NOPAUSE%"=="1" pause
endlocal
exit /b %ERR%
:::PAYLOAD:::
# Haraka Support - Windows agent installer (one-click, self-contained)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$Base = "http://162.0.225.39:8443"
$Token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiaW5zdGFsbCIsInN1YiI6ImFnZW50LWluc3RhbGwiLCJtYWNoaW5lSWQiOiJocy05ODgyNGJhZSIsImluc3RhbGxDb2RlIjoiWVNGVUYtM1k1RTIiLCJpYXQiOjE3ODYxOTEwMjgsImV4cCI6MTc4NjIxOTgyOH0.dXhpEsC9Sy9P4z3uHmUmmZO_Y5kmrti8W8bPuVY2cG8"
$Root = Join-Path $env:USERPROFILE "Haraka-Support"
$AgentDir = Join-Path $Root "agent"
$Zip = Join-Path $env:TEMP "haraka-support-agent.zip"
$LogsDir = Join-Path $Root "logs"
$EnvFile = Join-Path $Root ".env"
$LogFile = Join-Path $LogsDir "agent.log"

Write-Host "==> Haraka Support one-click install" -ForegroundColor Cyan
Write-Host "    Server: $Base"

function Refresh-Path {
  $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
}

function Get-NodeExe {
  Refresh-Path
  $cmd = Get-Command node -ErrorAction SilentlyContinue
  if ($cmd -and $cmd.Source) { return $cmd.Source }
  foreach ($p in @(
    (Join-Path $env:ProgramFiles "nodejs\node.exe"),
    (Join-Path ${env:ProgramFiles(x86)} "nodejs\node.exe"),
    (Join-Path $env:LOCALAPPDATA "Programs\nodejs\node.exe")
  )) {
    if ($p -and (Test-Path $p)) { return $p }
  }
  return $null
}

function Get-NpmCmd {
  Refresh-Path
  $cmd = Get-Command npm -ErrorAction SilentlyContinue
  if ($cmd -and $cmd.Source) { return $cmd.Source }
  $node = Get-NodeExe
  if ($node) {
    $npm = Join-Path (Split-Path $node) "npm.cmd"
    if (Test-Path $npm) { return $npm }
  }
  return $null
}

function Stop-HarakaAgent {
  Write-Host "==> Stopping any old agent..."
  try { Stop-ScheduledTask -TaskName "HarakaSupportAgent" -ErrorAction SilentlyContinue } catch {}
  try {
    Get-CimInstance Win32_Process -Filter "Name = 'node.exe'" -ErrorAction SilentlyContinue |
      Where-Object { $_.CommandLine -and ($_.CommandLine -like '*Haraka-Support*' -or $_.CommandLine -like '*haraka*') } |
      ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
  } catch {}
  Get-Process powershell -ErrorAction SilentlyContinue | Where-Object {
    try { $_.Path -and $_.MainWindowTitle -eq '' } catch { $false }
  } | Out-Null
  Start-Sleep -Seconds 1
}

function Install-NodeLts {
  Write-Host "==> Installing Node.js LTS (required)..." -ForegroundColor Yellow
  if (Get-Command winget -ErrorAction SilentlyContinue) {
    try {
      winget install -e --id OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements --silent
      Refresh-Path
      if (Get-NodeExe) { return }
    } catch {}
  }
  if (Get-Command choco -ErrorAction SilentlyContinue) {
    try {
      choco install nodejs-lts -y
      Refresh-Path
      if (Get-NodeExe) { return }
    } catch {}
  }
  # Direct MSI (works on many locked-down school PCs where winget is blocked)
  $msi = Join-Path $env:TEMP "haraka-node-lts.msi"
  $urls = @(
    "https://nodejs.org/dist/v22.17.0/node-v22.17.0-x64.msi",
    "https://nodejs.org/dist/v20.19.0/node-v20.19.0-x64.msi"
  )
  foreach ($u in $urls) {
    try {
      Write-Host "    Downloading $u"
      Invoke-WebRequest -Uri $u -OutFile $msi -UseBasicParsing
      $p = Start-Process -FilePath "msiexec.exe" -ArgumentList @("/i", $msi, "/qn", "/norestart") -Wait -PassThru
      Refresh-Path
      if ((Get-NodeExe) -and $p.ExitCode -eq 0) { return }
      if (Get-NodeExe) { return }
    } catch {
      Write-Host ("    MSI attempt failed: " + $_.Exception.Message) -ForegroundColor Yellow
    }
  }
}

$nodeExe = Get-NodeExe
if (-not $nodeExe) {
  Install-NodeLts
  $nodeExe = Get-NodeExe
}
if (-not $nodeExe) {
  Write-Host "ERROR: Node.js install failed. Open https://nodejs.org , install LTS, then double-click this installer again." -ForegroundColor Red
  exit 2
}
Write-Host "    Node: $nodeExe ($(& $nodeExe -v))" -ForegroundColor Green

Stop-HarakaAgent

Write-Host "==> Downloading agent package..."
Invoke-WebRequest -Uri "$Base/api/agent/download.zip?t=$([uri]::EscapeDataString($Token))" -OutFile $Zip -UseBasicParsing
if (-not (Test-Path $Zip) -or ((Get-Item $Zip).Length -lt 1000)) {
  Write-Host "ERROR: Agent download failed or too small." -ForegroundColor Red
  exit 1
}

if (-not (Test-Path $Root)) {
  New-Item -ItemType Directory -Path $Root | Out-Null
}

Write-Host "==> Extracting..."
$ExtractRoot = Join-Path $env:TEMP "Haraka-Support-extract"
if (Test-Path $ExtractRoot) { Remove-Item -Recurse -Force $ExtractRoot }
New-Item -ItemType Directory -Path $ExtractRoot | Out-Null
Expand-Archive -Path $Zip -DestinationPath $ExtractRoot -Force
$Extracted = Join-Path $ExtractRoot "Haraka-Support"
if (-not (Test-Path $Extracted)) {
  $cand = Get-ChildItem $ExtractRoot -Directory | Select-Object -First 1
  if ($cand) { $Extracted = $cand.FullName }
}
Copy-Item -Path (Join-Path $Extracted "*") -Destination $Root -Recurse -Force

Write-Host "==> Writing config (IP + credentials)..."
$Cfg = Invoke-RestMethod -Uri "$Base/api/agent/enroll?t=$([uri]::EscapeDataString($Token))" -Method GET
$Connect = if ($Cfg.serverUrl) { $Cfg.serverUrl } else { $Base }
$machineId = if ($Cfg.machineId) { $Cfg.machineId } else { $env:COMPUTERNAME }
$envLines = @(
  "SERVER_URL=$Connect",
  "AGENT_SERVER_URL=$Connect",
  "PUBLIC_URL=$Connect",
  "ADMIN_USERNAME=$($Cfg.username)",
  "ADMIN_PASSWORD=$($Cfg.password)",
  "AGENT_USERNAME=$($Cfg.username)",
  "AGENT_PASSWORD=$($Cfg.password)",
  "AGENT_MACHINE_ID=$machineId",
  "JWT_SECRET=agent-local-unused",
  "AGENT_FPS=18",
  "AGENT_STREAM_WIDTH=0",
  "AGENT_JPEG_QUALITY=60",
  "AGENT_CAPTURE_DISPLAY=auto"
)
$envLines -join [Environment]::NewLine | Set-Content -Path $EnvFile -Encoding UTF8
$cfgDir = Join-Path $Root "config"
New-Item -ItemType Directory -Path $cfgDir -Force | Out-Null
Set-Content -Path (Join-Path $cfgDir "machine-id.txt") -Value $machineId -Encoding UTF8
New-Item -ItemType Directory -Force -Path $LogsDir | Out-Null

Write-Host "==> Installing dependencies (display + mouse/keyboard)..."
Set-Location $AgentDir
# Force native addon install scripts (npm 11+ allowScripts warnings must not abort setup)
@(
  "ignore-scripts=false",
  "foreground-scripts=true",
  "dangerously-allow-all-scripts=true"
) | Set-Content -Path (Join-Path $AgentDir ".npmrc") -Encoding ASCII
$npmCmd = Get-NpmCmd
if (-not $npmCmd) {
  Write-Host "ERROR: npm not found next to Node. Reinstall Node.js LTS." -ForegroundColor Red
  exit 2
}
$env:npm_config_ignore_scripts = "false"
$env:npm_config_dangerously_allow_all_scripts = "true"

# Run npm via cmd.exe so PowerShell does not treat npm stderr warnings as fatal.
function Invoke-NpmCmd {
  param([Parameter(Mandatory=$true)][string]$ArgLine)
  $prev = $ErrorActionPreference
  $ErrorActionPreference = "Continue"
  try {
    $quotedNpm = '"' + $npmCmd + '"'
    cmd.exe /c "$quotedNpm $ArgLine"
    return $LASTEXITCODE
  } finally {
    $ErrorActionPreference = $prev
  }
}

$code = Invoke-NpmCmd "install --no-fund --no-audit --foreground-scripts"
if ($code -ne 0) {
  Write-Host "npm install failed - retrying once..." -ForegroundColor Yellow
  $code = Invoke-NpmCmd "install --no-fund --no-audit --foreground-scripts"
  if ($code -ne 0) { exit $code }
}

# Approve koffi scripts (npm 11+) then rebuild native bindings. Warnings are OK.
Write-Host "==> Building native mouse/keyboard bindings..."
$ErrorActionPreference = "Continue"
try { Invoke-NpmCmd "approve-scripts koffi --no-allow-scripts-pin" | Out-Null } catch {}
try { Invoke-NpmCmd "approve-scripts --all --no-allow-scripts-pin" | Out-Null } catch {}
Invoke-NpmCmd "rebuild koffi --foreground-scripts" | Out-Null
Invoke-NpmCmd "install koffi@^2.9.0 --no-fund --no-audit --foreground-scripts" | Out-Null
$ErrorActionPreference = "Stop"

Write-Host "==> Verifying instant Win32 input..."
$prevEa = $ErrorActionPreference
$ErrorActionPreference = "Continue"
& $nodeExe -e "const k=require('koffi'); const u=k.load('user32.dll'); u.func('bool SetCursorPos(int X, int Y)'); require('ws'); require('screenshot-desktop'); console.log('koffi-native-ok')"
$verifyCode = $LASTEXITCODE
$ErrorActionPreference = $prevEa
if ($verifyCode -ne 0) {
  Write-Host "WARNING: koffi native check failed - trying PowerShell input host fallback." -ForegroundColor Yellow
} else {
  Write-Host "    koffi native OK" -ForegroundColor Green
}
if (-not (Test-Path (Join-Path $AgentDir "tools\win-input-host.ps1"))) {
  Write-Host "WARNING: win-input-host.ps1 missing from package." -ForegroundColor Yellow
}

Write-Host "==> Registering auto-start (task + Startup folder)..."
$psExe = (Get-Command powershell).Source
$wrapper = Join-Path $AgentDir "run-agent.ps1"
$taskName = "HarakaSupportAgent"
try {
  Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
  $arg = '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "' + $wrapper + '"'
  $action = New-ScheduledTaskAction -Execute $psExe -Argument $arg -WorkingDirectory $AgentDir
  $triggerLogon = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
  $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Days 3650) -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) -MultipleInstances IgnoreNew
  $principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive -RunLevel Limited
  Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $triggerLogon -Settings $settings -Principal $principal -Description "Haraka Support agent auto-start" | Out-Null
  Write-Host "    Scheduled task: $taskName" -ForegroundColor Green
} catch {
  Write-Host ("    Scheduled task skipped: " + $_.Exception.Message) -ForegroundColor Yellow
}
$startupDir = Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs\Startup"
New-Item -ItemType Directory -Force -Path $startupDir | Out-Null
$startupCmd = Join-Path $startupDir "HarakaSupportAgent.cmd"
$startupBody = @(
  "@echo off",
  ('cd /d "' + $AgentDir + '"'),
  ('start "" /MIN "' + $psExe + '" -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "' + $wrapper + '"')
) -join [Environment]::NewLine
Set-Content -Path $startupCmd -Value $startupBody -Encoding ASCII
Write-Host "    Startup folder: OK" -ForegroundColor Green

Write-Host "==> Starting agent..."
Start-Process -FilePath $psExe -ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-File", $wrapper) -WorkingDirectory $AgentDir -WindowStyle Hidden | Out-Null
try { Start-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue } catch {}

Write-Host "==> Waiting for agent to come Online..."
$online = $false
for ($i = 0; $i -lt 24; $i++) {
  Start-Sleep -Seconds 2
  if (Test-Path $LogFile) {
    $tail = Get-Content $LogFile -Tail 40 -ErrorAction SilentlyContinue | Out-String
    if ($tail -match 'Connected|WebSocket|agent online|registered|hello') {
      $online = $true
      break
    }
    if ($tail -match 'ECONNREFUSED|ENOTFOUND|login failed|Missing agent') {
      Write-Host "    Still connecting... ($($i+1))" -ForegroundColor Yellow
    }
  }
  $proc = Get-CimInstance Win32_Process -Filter "Name = 'node.exe'" -ErrorAction SilentlyContinue |
    Where-Object { $_.CommandLine -and $_.CommandLine -like '*Haraka-Support*\agent*' }
  if ($proc) { $online = $true }
}

Write-Host ""
Write-Host "==============================================" -ForegroundColor Green
Write-Host " ONE-CLICK INSTALL COMPLETE"
Write-Host " Machine ID:  $machineId"
Write-Host " Server:      $Connect"
if ($online) {
  Write-Host " Agent:       running - open console and Connect" -ForegroundColor Green
} else {
  Write-Host " Agent:       started (may take a few more seconds)" -ForegroundColor Yellow
  Write-Host " If Offline, stay signed in on this desktop and wait 30s."
}
Write-Host " Display + mouse + keyboard are included in this install."
Write-Host "=============================================="

