#!/usr/bin/env bash
# Full deployment: pull images → refresh env → run migrations → restart services → health check
set -euo pipefail

IMAGE_TAG="${1:?Usage: deploy.sh <image-tag>}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RELEASE_FILE="/opt/helix/releases/current"

# Per-step timing. The SSM Run Command buffers all stdout and the GHA
# deploy job only sees it after the script finishes, so without explicit
# timestamps the ~9-min on-host runtime is an opaque blob. ``step`` prints
# wall-clock + cumulative + per-step delta so the next deploy's log shows
# exactly where the time goes (pull vs migrate vs recreate vs healthcheck).
_DEPLOY_T0=$(date +%s)
_STEP_TS=$_DEPLOY_T0
step() {
  local now elapsed delta
  now=$(date +%s)
  elapsed=$(( now - _DEPLOY_T0 ))
  delta=$(( now - _STEP_TS ))
  echo "[$(date -u +%H:%M:%S)Z | t+${elapsed}s | prev-step ${delta}s] $*"
  _STEP_TS=$now
}

echo "=== Deploying Helix (tag: ${IMAGE_TAG}) ==="

# Record previous tag for rollback
PREVIOUS_TAG=""
if [[ -f "$RELEASE_FILE" ]]; then
  PREVIOUS_TAG=$(cat "$RELEASE_FILE")
  echo "Previous release: ${PREVIOUS_TAG}"
fi

# Step 1: Pull images
step "--- Step 1/5: Pulling images ---"
"$SCRIPT_DIR/pull-images.sh" "$IMAGE_TAG"

# Step 2: Refresh runtime environment from SSM
step "--- Step 2/5: Rendering runtime env ---"
"$SCRIPT_DIR/render-env.sh"

# Step 3: Run migrations
step "--- Step 3/5: Running migrations ---"
"$SCRIPT_DIR/run-migrations.sh" "$IMAGE_TAG"

# Step 4: Restart services
step "--- Step 4/5: Restarting services ---"
# Tenant CA certificates are public trust anchors, not secrets, but keeping the
# host directory explicit prevents Docker from silently creating a missing bind
# source with surprising ownership. Operators place approved .pem/.crt files
# here; backend connection tests and worker syncs see the same read-only set.
install -d -m 0755 /opt/helix/ca-certificates
cd /opt/helix/compose
# ``--force-recreate``: defensive hardening so every deploy restarts
# backend+worker from the freshly-pulled image regardless of how the
# image is tagged. Auto-deploys pass an immutable ``sha-<...>`` tag, so a
# plain ``up -d`` already recreates on the changed reference — but a
# MANUAL ``Production Deploy`` dispatch defaults to the mutable ``main``
# tag (prod-deploy.yml), where recreate depends on compose detecting the
# digest change. ``--force-recreate`` removes that ambiguity: the worker
# always comes up on the deployed code so newly-added scheduled-job
# handlers register on the next deploy rather than only on the next
# unrelated restart. (NOTE: this is hardening, not the fix for the
# 2026-06-03 stranded-cron incident — that was malformed no-dash UUIDs
# in the seed migrations; see migration 247.)
docker compose -f docker-compose.prod.yml up -d --remove-orphans --force-recreate
# Force-restart nginx so it re-resolves the backend container IP (changes on every backend recreate)
docker compose -f docker-compose.prod.yml restart frontend

# Step 5: Health check
step "--- Step 5/5: Health check ---"
if "$SCRIPT_DIR/healthcheck.sh"; then
  # Record successful release
  echo "$IMAGE_TAG" > "$RELEASE_FILE"
  # Reclaim disk now that the new images are attached to running
  # containers — Docker's "in use" guard protects the just-deployed
  # release while ``-af`` frees the previous sha-tagged images. Doing
  # this here (not before the pull) is what lets pull-images.sh keep the
  # prepull-warmed layers. Best-effort: never fail a healthy deploy on a
  # prune hiccup.
  step "--- Post-deploy: reclaiming old images ---"
  docker image prune -af || true
  step "=== Deployment successful (tag: ${IMAGE_TAG}) — total $(( $(date +%s) - _DEPLOY_T0 ))s ==="
else
  echo "=== Health check FAILED ==="
  if [[ -n "$PREVIOUS_TAG" ]]; then
    echo "Rolling back to ${PREVIOUS_TAG}..."
    "$SCRIPT_DIR/rollback.sh" "$PREVIOUS_TAG"
    echo "Rollback succeeded, but the requested release ${IMAGE_TAG} was NOT deployed." >&2
    # Preserve the original deployment failure for SSM/GitHub Actions.
    # A successful rollback restores availability; it does not make the
    # attempted release successful.  Exit 2 gives the workflow a stable
    # machine-readable "rolled back" outcome distinct from a broken rollback.
    exit 2
  else
    echo "No previous release to rollback to. Manual intervention required."
    exit 1
  fi
fi
