<#
.SYNOPSIS
  Build and deploy the Lean 101 Clients app to a Digital Ocean droplet over SSH.

.DESCRIPTION
  Runs `docker compose` against `docker-compose.production.yml` on the remote
  host. The same script handles first-time bootstrap and subsequent updates:

    * On bootstrap (-Bootstrap): creates the remote directory, clones the
      repo (or updates if already present), uploads the local env file, and
      brings the stack up with `docker compose ... up -d --build`.

    * On update (default): SSHes to the host, fetches the requested branch,
      uploads a refreshed env file (if changed), then runs
      `docker compose ... up -d --build` followed by a healthcheck.

  The script never executes destructive commands without asking, except for
  recreating containers (which preserves the named Postgres volume).

.PARAMETER RemoteHost
  Hostname or IP of the Digital Ocean droplet. Required.

.PARAMETER RemoteUser
  SSH user on the droplet. Defaults to `root`.

.PARAMETER RemotePath
  Absolute path on the droplet where the repo lives. Defaults to
  `/srv/lean101-clients`.

.PARAMETER Branch
  Git branch to deploy. Defaults to `main`.

.PARAMETER RepoUrl
  Git URL used during bootstrap when the remote directory is empty.
  Required only with -Bootstrap.

.PARAMETER EnvFile
  Local path to the env file that should land on the droplet as
  `<RemotePath>/.env.production`. Defaults to `.env.production`.

.PARAMETER SshKey
  Optional path to an SSH private key. If omitted, the script relies on
  ssh-agent / default keys.

.PARAMETER ComposeFile
  Compose file name on the remote host. Defaults to
  `docker-compose.production.yml`.

.PARAMETER Bootstrap
  Run first-time setup (clone, upload env, build, up).

.PARAMETER SkipBuild
  Pass `--no-build` to docker compose (use when only env changed).

.PARAMETER Seed
  Run `python -m app.seed` inside the backend container after the stack is up.

.PARAMETER Logs
  After deploy, tail logs for ~20 lines so you can verify the stack came up.

.EXAMPLE
  ./deploy/Deploy.ps1 -RemoteHost 203.0.113.10 -Bootstrap -RepoUrl git@github.com:ponzischeme89/data-entry-app.git

.EXAMPLE
  ./deploy/Deploy.ps1 -RemoteHost 203.0.113.10
#>

[CmdletBinding()]
param(
  [Parameter(Mandatory = $true)] [string] $RemoteHost,
  [string] $RemoteUser = "root",
  [string] $RemotePath = "/srv/lean101-clients",
  [string] $Branch = "main",
  [string] $RepoUrl,
  [string] $EnvFile = ".env.production",
  [string] $SshKey,
  [string] $ComposeFile = "docker-compose.production.yml",
  [switch] $Bootstrap,
  [switch] $SkipBuild,
  [switch] $Seed,
  [switch] $Logs
)

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

function Write-Step($message) {
  Write-Host "==> $message" -ForegroundColor Cyan
}

function Write-Warn($message) {
  Write-Host "!! $message" -ForegroundColor Yellow
}

function Resolve-RepoRoot {
  $scriptDir = Split-Path -Parent $MyInvocation.ScriptName
  if (-not $scriptDir) { $scriptDir = $PSScriptRoot }
  return (Resolve-Path (Join-Path $scriptDir "..")).Path
}

$RepoRoot = Resolve-RepoRoot
Push-Location $RepoRoot
try {
  $envPath = if ([System.IO.Path]::IsPathRooted($EnvFile)) { $EnvFile } else { Join-Path $RepoRoot $EnvFile }
  if (-not (Test-Path $envPath)) {
    throw "Env file not found at '$envPath'. Copy .env.production.example to $EnvFile and fill in production secrets first."
  }

  $sshTarget = "$RemoteUser@$RemoteHost"
  $sshOpts = @("-o", "StrictHostKeyChecking=accept-new")
  if ($SshKey) { $sshOpts += @("-i", $SshKey) }

  function Invoke-Ssh([string] $remoteCommand) {
    & ssh @sshOpts $sshTarget $remoteCommand
    if ($LASTEXITCODE -ne 0) {
      throw "Remote command failed (exit $LASTEXITCODE): $remoteCommand"
    }
  }

  function Invoke-Scp([string] $localPath, [string] $remoteDest) {
    & scp @sshOpts $localPath "$($sshTarget):$remoteDest"
    if ($LASTEXITCODE -ne 0) {
      throw "scp failed for $localPath -> $remoteDest"
    }
  }

  Write-Step "Verifying SSH connectivity to $sshTarget"
  Invoke-Ssh "echo connected as `$(whoami) on `$(hostname)"

  Write-Step "Verifying Docker is installed on the droplet"
  Invoke-Ssh "command -v docker >/dev/null 2>&1 && docker --version && docker compose version"

  if ($Bootstrap) {
    if (-not $RepoUrl) {
      throw "-RepoUrl is required when using -Bootstrap."
    }
    Write-Step "Bootstrapping $RemotePath from $RepoUrl ($Branch)"
    $bootstrapScript = @"
set -euo pipefail
mkdir -p '$RemotePath'
cd '$RemotePath'
if [ ! -d .git ]; then
  git clone --branch '$Branch' '$RepoUrl' .
else
  git remote set-url origin '$RepoUrl'
  git fetch origin '$Branch'
  git checkout '$Branch'
  git reset --hard 'origin/$Branch'
fi
"@
    Invoke-Ssh $bootstrapScript
  } else {
    Write-Step "Updating $RemotePath to latest $Branch"
    $updateScript = @"
set -euo pipefail
cd '$RemotePath'
git fetch origin '$Branch'
git checkout '$Branch'
git reset --hard 'origin/$Branch'
"@
    Invoke-Ssh $updateScript
  }

  Write-Step "Uploading $EnvFile to $RemotePath/.env.production"
  Invoke-Scp $envPath "$RemotePath/.env.production"
  Invoke-Ssh "chmod 600 '$RemotePath/.env.production'"

  $composeArgs = @(
    "--env-file", ".env.production",
    "-f", $ComposeFile
  ) -join " "

  $buildFlag = if ($SkipBuild) { "" } else { "--build" }

  Write-Step "Pulling base images"
  Invoke-Ssh "cd '$RemotePath' && docker compose $composeArgs pull --ignore-pull-failures || true"

  Write-Step "Bringing the stack up (build=$([bool](-not $SkipBuild)))"
  Invoke-Ssh "cd '$RemotePath' && docker compose $composeArgs up -d $buildFlag --remove-orphans"

  Write-Step "Waiting for backend healthcheck"
  $healthScript = @"
set -e
cd '$RemotePath'
for attempt in `$(seq 1 30); do
  status=`$(docker inspect --format='{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' lean101-clients-backend 2>/dev/null || echo missing)
  case "`$status" in
    healthy|running)
      echo "backend is `$status"
      exit 0;;
    *)
      printf '.'
      sleep 4;;
  esac
done
echo
echo 'backend did not become healthy in time' >&2
docker compose $composeArgs ps backend
exit 1
"@
  Invoke-Ssh $healthScript

  if ($Seed) {
    Write-Step "Seeding reference data"
    Invoke-Ssh "cd '$RemotePath' && docker compose $composeArgs exec -T backend python -m app.seed"
  }

  Write-Step "Final container status"
  Invoke-Ssh "cd '$RemotePath' && docker compose $composeArgs ps"

  if ($Logs) {
    Write-Step "Recent logs (last 60 lines)"
    Invoke-Ssh "cd '$RemotePath' && docker compose $composeArgs logs --tail=60"
  }

  Write-Host "Deployment complete." -ForegroundColor Green
}
finally {
  Pop-Location
}
