#!/usr/bin/env bash
###############################################################################
# rollout.sh - post-deploy pipeline for DeployHQ "After Changes" SSH commands
#
# Runs AS THE DEPLOY USER (acdev3 on dev, amsoildlp on staging/production).
# The only privileged step, reloading root's PM2 daemon, is delegated to a
# root-owned wrapper through a narrow sudoers rule:
#
#   sudo -n /usr/local/sbin/amsoil-pm2-reload <env>
#
# One-time root setup (wrapper install + sudoers) and the DeployHQ command for
# each server: docs/plans/2026-07-09-deployhq-rollout-automation.md
#
# Usage: bash scripts/server/rollout.sh <dev|staging|production>
###############################################################################
set -euo pipefail

ENV_NAME="${1:?usage: rollout.sh <dev|staging|production>}"

case "$ENV_NAME" in
  dev)
    APP_DIR=/home/acdev3/amsoil-dlp.acdev3.com
    APP_USER=acdev3
    RUN_BUILD=false # dev runs `next dev`, which compiles on demand; no build step
    ;;
  staging)
    APP_DIR=/home/amsoildlp/public_html/aimclear.biz
    APP_USER=amsoildlp
    RUN_BUILD=true
    ;;
  production)
    APP_DIR=/home/amsoildlp/public_html/amsoil.aimclear.com
    APP_USER=amsoildlp
    RUN_BUILD=true
    ;;
  *)
    echo "rollout.sh: unknown environment '$ENV_NAME'" >&2
    exit 2
    ;;
esac

if [ "$(id -un)" != "$APP_USER" ]; then
  echo "rollout.sh: $ENV_NAME must run as $APP_USER, not $(id -un)" >&2
  exit 3
fi

cd "$APP_DIR"

# Staging and production share one host AND one deploy user (amsoildlp), so
# $HOME resolves to the same lock file for both and a staging rollout blocks
# a production rollout (and vice versa). That is intentional: it serializes
# `next build` runs that would otherwise compete for memory on the shared
# box. Dev has its own user/home and therefore its own lock.
exec 9>"$HOME/.amsoil-rollout.lock"
if ! flock -w 900 9; then
  echo "rollout.sh: another rollout held the lock for >15 min, aborting" >&2
  exit 4
fi

# npm install (not npm ci): matches the existing DeployHQ pipelines and stays
# incremental on the shared host; npm ci would wipe node_modules every deploy.
echo "==> [$ENV_NAME] npm install"
npm install

echo "==> [$ENV_NAME] prisma generate"
npx prisma generate

# Migrations run BEFORE the build: `next build` prerenders force-static pages
# that query the database (e.g. the directory hub via getDealersForApex), so
# building against the old schema would fail any deploy whose build-time
# queries use new columns/tables. The old process keeps serving while this
# runs, so migrations must stay backward compatible (expand/contract). If
# migrate fails, the old code keeps serving against a partially migrated
# database and the box needs manual follow-up before redeploying.
echo "==> [$ENV_NAME] prisma migrate deploy"
npx prisma migrate deploy

if [ "$RUN_BUILD" = true ]; then
  # Build OUT OF PLACE, then swap atomically. Building straight into the live
  # .next deletes chunks the running server still needs, erroring SSR requests
  # (dealer pages) for the entire ~6 minute build - observed on production
  # 2026-07-13.
  #
  # The build MUST run with the default distDir '.next': Next inlines
  # config.distDir into every compiled route module at build time
  # (process.env.__NEXT_RELATIVE_DIST_DIR in next/dist/build/define-env.js),
  # so a build made with a different distDir breaks after being renamed.
  # Therefore the build runs in a hardlink-mirrored sibling WORKSPACE whose
  # own .next starts empty, and the finished .next is then moved into the
  # live tree. Hardlinks copy no file data; the workspace costs seconds and
  # only inodes. The swap is two renames (milliseconds), followed immediately
  # by the reload below. .next-prev is kept as a one-generation rollback
  # (restore: mv .next .next-bad && mv .next-prev .next, then reload).
  if ! command -v rsync >/dev/null 2>&1; then
    echo "rollout.sh: rsync is required for the out-of-place build" >&2
    exit 5
  fi

  WORKSPACE="$(dirname "$APP_DIR")/.rollout-build-$ENV_NAME"
  # Clean up the workspace however the script exits: success (its .next has
  # been moved out by then), a failed build, or a kill mid-build.
  trap 'rm -rf "$WORKSPACE"' EXIT
  echo "==> [$ENV_NAME] preparing build workspace ($WORKSPACE)"
  rm -rf "$WORKSPACE"
  rsync -a --link-dest="$APP_DIR" \
    --exclude='/.next' --exclude='/.next-prev' --exclude='/uploads' \
    "$APP_DIR/" "$WORKSPACE/"

  # The workspace's node_modules is hardlinked to the live tree's. The
  # prebuild hook regenerates the Prisma client inside the workspace;
  # prisma 7 unlinks-and-recreates (verified: new inodes), which is
  # hardlink-safe, but drop the GENERATED dir anyway so regeneration can
  # never write through a shared inode into the live tree. Only .prisma is
  # generated output - node_modules/@prisma/client is the npm package
  # itself, and removing it breaks `prisma generate` entirely
  # ("Could not resolve @prisma/client", staging deploy 9aef428a).
  rm -rf "$WORKSPACE/node_modules/.prisma"

  echo "==> [$ENV_NAME] next build (in workspace)"
  (cd "$WORKSPACE" && npm run build)

  # Fail closed: a killed or partial build must never reach the swap. BUILD_ID
  # alone is written early, so also require the prerender manifest, which the
  # build writes near the end.
  if [ ! -f "$WORKSPACE/.next/BUILD_ID" ] || [ ! -f "$WORKSPACE/.next/prerender-manifest.json" ]; then
    echo "rollout.sh: workspace build is missing BUILD_ID or prerender-manifest.json;" >&2
    echo "rollout.sh: build looks incomplete - refusing to swap, old build keeps serving" >&2
    rm -rf "$WORKSPACE"
    exit 6
  fi

  echo "==> [$ENV_NAME] swapping new build live"
  rm -rf "$APP_DIR/.next-prev"
  if [ -d "$APP_DIR/.next" ]; then mv "$APP_DIR/.next" "$APP_DIR/.next-prev"; fi
  mv "$WORKSPACE/.next" "$APP_DIR/.next"
  rm -rf "$WORKSPACE"
fi

# NOTE (dev): never delete .next here - this script runs while the app is
# live, and removing .next under a running `next dev` 500s the site. The
# wrapper clears it for dev, safely, between pm2 stop and start.
echo "==> [$ENV_NAME] pm2 reload via root wrapper"
sudo -n /usr/local/sbin/amsoil-pm2-reload "$ENV_NAME"

echo "==> [$ENV_NAME] rollout complete"
