#!/usr/bin/env bash
set -Eeuo pipefail

USAGE="Usage: activate-release.sh APP_ROOT RELEASE_ID ARCHIVE [PORT] [PM2_APP_SUFFIX]"
APP_ROOT="${1:?$USAGE}"
RELEASE_ID="${2:?$USAGE}"
ARCHIVE="${3:?$USAGE}"
export PORT="${4:-3000}"
export PM2_APP_SUFFIX="${5:-}"
RELEASE_DIR="$APP_ROOT/releases/$RELEASE_ID"
CURRENT_LINK="$APP_ROOT/current"
PREVIOUS_RELEASE=""

# WHM/cPanel Node Selector installs typically don't put node/npm/pm2 on PATH
# for a non-interactive SSH session. Drop a `source ...` line (e.g. the
# nodevenv activate script) in shared/env.sh once per APP_ROOT to fix that.
if [[ -f "$APP_ROOT/shared/env.sh" ]]; then
  # shellcheck disable=SC1091
  source "$APP_ROOT/shared/env.sh"
fi

if [[ ! "$RELEASE_ID" =~ ^[0-9a-f]{40}$ ]]; then
  echo "Invalid release id: $RELEASE_ID" >&2
  exit 1
fi

if [[ -L "$CURRENT_LINK" ]]; then
  PREVIOUS_RELEASE="$(readlink -f "$CURRENT_LINK")"
fi

# `pm2 startOrReload`/`pm2 reload` do not reliably swap in the new build on
# this box: it reports success and cycles PIDs, but the previously running
# code keeps serving indefinitely (confirmed live via a diagnostic endpoint
# that kept reporting stale env after repeated reloads). A full delete+start
# forces a genuinely fresh process, which is the only thing that actually
# worked. This costs a few seconds of downtime during restart instead of
# PM2's zero-downtime reload, but a fast restart with correct code beats a
# zero-downtime restart that silently keeps serving the old release.
pm2_hard_restart() {
  pm2 delete "web${PM2_APP_SUFFIX}" "worker${PM2_APP_SUFFIX}" >/dev/null 2>&1 || true
  pm2 start ecosystem.config.cjs
}

rollback() {
  exit_code=$?
  if [[ -n "$PREVIOUS_RELEASE" && -d "$PREVIOUS_RELEASE" ]]; then
    echo "Deployment failed; restoring $PREVIOUS_RELEASE"
    ln -sfn "$PREVIOUS_RELEASE" "$CURRENT_LINK"
    cd "$PREVIOUS_RELEASE"
    pm2_hard_restart || true
  fi
  exit "$exit_code"
}
trap rollback ERR

mkdir -p "$RELEASE_DIR"
tar -xzf "$ARCHIVE" -C "$RELEASE_DIR"
rm -f "$ARCHIVE"

if [[ ! -f "$APP_ROOT/shared/.env.production" ]]; then
  echo "Missing $APP_ROOT/shared/.env.production" >&2
  exit 1
fi

ln -sfn "$APP_ROOT/shared/.env.production" "$RELEASE_DIR/.env.production"

cd "$RELEASE_DIR"
npm ci
npm run build

# `npm run build` picks up .env.production via Next.js's own env loading, but
# the Prisma CLI below only auto-loads a file literally named .env, so the
# vars need to be exported into the shell explicitly for it to see them.
set -a
# shellcheck disable=SC1091
source "$APP_ROOT/shared/.env.production"
set +a
npx prisma migrate deploy

ln -sfn "$RELEASE_DIR" "$CURRENT_LINK"
pm2_hard_restart

for attempt in {1..20}; do
  if curl --silent --show-error --fail --max-time 5 \
    "http://127.0.0.1:$PORT/api/health" >/dev/null; then
    trap - ERR
    echo "Release $RELEASE_ID is healthy"
    break
  fi

  if [[ "$attempt" == 20 ]]; then
    echo "Health check failed after 20 attempts" >&2
    false
  fi
  sleep 3
done

# Keep the current release plus the four most recent older releases.
while IFS= read -r old_release; do
  [[ "$old_release" == "$RELEASE_DIR" ]] || rm -rf -- "$old_release"
done < <(find "$APP_ROOT/releases" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\n' \
  | sort -nr | tail -n +6 | cut -d' ' -f2-)

pm2 save
