<#
  Deploys the Purple Envelope Orders Site to the production IIS server.

  Flow:
  1. Build a release locally (scripts\build-release.ps1) - a clean file tree with the
     production web.config swapped in, and dev-only files excluded via .gitattributes.
  2. Zip it and scp the zip to the remote host.
  3. scp scripts\deploy-iis-remote-apply.ps1 to the remote host and run it over ssh - it
     wipes the deploy directory, extracts the new release, points IIS at it, runs
     migrations, and restarts the site/app pool. The database and error log live outside
     the deploy directory, so the wipe never touches them.
  4. Smoke-test a few routes over HTTPS from this machine.

  Usage:
    powershell -File scripts\deploy-iis.ps1
    powershell -File scripts\deploy-iis.ps1 -Ref master

  Remote target defaults to the "ssh user@host" line in scripts\deployinfo.txt (gitignored,
  not committed - create it locally, e.g.:  ssh daniel_admin@kci-uluto-web).
#>

param(
  [string]$Ref = 'HEAD',
  [string]$RemoteTarget = '',
  [int]$RemotePort = 22,
  [string]$RemoteDir = 'C:\inetpub\wwwroot\Purple_Envelop_Order_Site',
  [string]$SiteName = 'PurpleEnvelopes',
  [string]$AppPool = 'PurpleEnvelopes',
  [string]$AdminSiteName = '',
  [string]$AdminAppPool = '',
  [string]$DbPath = 'C:\inetpub\data\webdata.accdb',
  [string]$BaseUrl = 'https://pe.kentcommunications.com/',
  [string]$AdminBaseUrl = '',
  [switch]$RunMigrations = $true,
  [string]$SshExe = 'ssh',
  [string]$ScpExe = 'scp'
)

$ErrorActionPreference = 'Stop'
$repoRoot = Split-Path $PSScriptRoot -Parent

function Ensure-Command {
  param([string]$Name)
  if(!(Get-Command $Name -ErrorAction SilentlyContinue)){
    throw "$Name not found on PATH"
  }
}

function Get-DefaultRemoteTarget {
  $infoPath = Join-Path $PSScriptRoot 'deployinfo.txt'
  if(!(Test-Path $infoPath)){ return '' }
  $sshLine = Get-Content $infoPath | Where-Object { $_ -match '^\s*ssh\s+' } | Select-Object -First 1
  if([string]::IsNullOrWhiteSpace($sshLine)){ return '' }
  return ($sshLine -replace '^\s*ssh\s+', '').Trim()
}

if([string]::IsNullOrWhiteSpace($RemoteTarget)){
  $RemoteTarget = Get-DefaultRemoteTarget
}
if([string]::IsNullOrWhiteSpace($RemoteTarget)){
  throw 'No -RemoteTarget given and scripts\deployinfo.txt not found. Create scripts\deployinfo.txt with a line like: ssh user@host'
}

Ensure-Command $SshExe
Ensure-Command $ScpExe
Ensure-Command git

# --- 1. Build ---
$outDir = Join-Path $repoRoot ('dist\release_' + (Get-Date -Format 'yyyyMMdd_HHmmss'))
& (Join-Path $PSScriptRoot 'build-release.ps1') -Ref $Ref -OutDir $outDir
if($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne $null){ throw 'build-release.ps1 failed' }

# --- 2. Zip and ship it ---
$zipName = 'release_' + (Get-Date -Format 'yyyyMMdd_HHmmss') + '.zip'
$localZip = Join-Path (Split-Path $outDir -Parent) $zipName
Compress-Archive -Path (Join-Path $outDir '*') -DestinationPath $localZip -Force

$remoteZip = 'C:\Windows\Temp\' + $zipName
$remoteApplyScript = Join-Path $PSScriptRoot 'deploy-iis-remote-apply.ps1'
$remoteApplyDest = 'C:\Windows\Temp\deploy-iis-remote-apply.ps1'

# Force a fall-through to password auth: if pubkey auth isn't set up (or an agent offers a
# key the server doesn't accept), plain ssh/scp can otherwise fail outright with
# "Permission denied (publickey)" instead of ever prompting for a password.
$AuthOpts = @(
  '-o', 'PreferredAuthentications=publickey,keyboard-interactive,password',
  '-o', 'NumberOfPasswordPrompts=3'
)

Write-Host "Copying release to $RemoteTarget"
& $ScpExe -P $RemotePort @AuthOpts $localZip "${RemoteTarget}:$remoteZip"
if($LASTEXITCODE -ne 0){ throw 'scp of release zip failed - see scp output above for the actual reason' }

& $ScpExe -P $RemotePort @AuthOpts $remoteApplyScript "${RemoteTarget}:$remoteApplyDest"
if($LASTEXITCODE -ne 0){ throw 'scp of remote-apply script failed - see scp output above for the actual reason' }

# --- 3. Apply on the remote host ---
$remoteCommandParts = @(
  'powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass',
  '-File', ('"' + $remoteApplyDest + '"'),
  '-ZipPath', ('"' + $remoteZip + '"'),
  '-RemoteDir', ('"' + $RemoteDir + '"'),
  '-SiteName', ('"' + $SiteName + '"'),
  '-AppPool', ('"' + $AppPool + '"'),
  '-DbPath', ('"' + $DbPath + '"')
)
if(![string]::IsNullOrWhiteSpace($AdminSiteName)){
  $remoteCommandParts += @('-AdminSiteName', ('"' + $AdminSiteName + '"'))
}
if(![string]::IsNullOrWhiteSpace($AdminAppPool)){
  $remoteCommandParts += @('-AdminAppPool', ('"' + $AdminAppPool + '"'))
}
if($RunMigrations){ $remoteCommandParts += '-RunMigrations' }

Write-Host "Applying release on $RemoteTarget"
& $SshExe -p $RemotePort @AuthOpts $RemoteTarget ($remoteCommandParts -join ' ')
if($LASTEXITCODE -ne 0){ throw 'remote apply failed - see ssh output above for the actual reason' }

# --- 4. Local cleanup ---
Remove-Item $localZip -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force $outDir -ErrorAction SilentlyContinue

# --- 5. Smoke test ---
Write-Host 'Smoke testing...'
# /404 is the app's own not-found route - a 404 there is correct, not a failure.
$checks = @(
  @{ Path = '/'; Expect = 200 },
  @{ Path = '/request-order'; Expect = 200 },
  @{ Path = '/404'; Expect = 404 }
)
$failed = $false
foreach($check in $checks){
  $url = $BaseUrl.TrimEnd('/') + $check.Path
  try {
    $response = Invoke-WebRequest -UseBasicParsing -Uri $url -TimeoutSec 30
    $status = [int]$response.StatusCode
  } catch {
    if($_.Exception.Response){
      $status = [int]$_.Exception.Response.StatusCode
    } else {
      Write-Host ("FAIL " + $check.Path + ' -> request failed: ' + $_.Exception.Message)
      $failed = $true
      continue
    }
  }

  if($status -eq $check.Expect){
    Write-Host ("OK   " + $check.Path + ' -> ' + $status)
  } else {
    Write-Host ("FAIL " + $check.Path + ' -> ' + $status + ' (expected ' + $check.Expect + ')')
    $failed = $true
  }
}

if($failed){
  throw 'Smoke test failed - see above'
}

# --- 6. Admin site smoke test ---
if(![string]::IsNullOrWhiteSpace($AdminBaseUrl)){
  Write-Host 'Smoke testing admin site...'
  $adminChecks = @(
    @{ Path = '/'; Expect = 200 }
  )
  foreach($check in $adminChecks){
    $url = $AdminBaseUrl.TrimEnd('/') + $check.Path
    try {
      $response = Invoke-WebRequest -UseBasicParsing -Uri $url -TimeoutSec 30
      $status = [int]$response.StatusCode
    } catch {
      if($_.Exception.Response){
        $status = [int]$_.Exception.Response.StatusCode
      } else {
        Write-Host ("FAIL (admin) " + $check.Path + ' -> request failed: ' + $_.Exception.Message)
        $failed = $true
        continue
      }
    }
    if($status -eq $check.Expect){
      Write-Host ("OK   (admin) " + $check.Path + ' -> ' + $status)
    } else {
      Write-Host ("FAIL (admin) " + $check.Path + ' -> ' + $status + ' (expected ' + $check.Expect + ')')
      $failed = $true
    }
  }
  if($failed){
    throw 'Admin smoke test failed - see above'
  }
}

Write-Host 'Deploy complete.'
