#!/usr/bin/env bash
#
# One-shot live-data migration from the OLD production VPS to the NEW EC2 host.
# Streams the Postgres database and the `uploads_data` docker volume directly
# OLD -> NEW (piped through this machine; no multi-GB files staged on either
# server). Run this AFTER the NEW host already has a healthy, EMPTY stack up
# (i.e. after `make deploy-init && make redeploy` against the EC2 target).
#
# Usage:
#   scripts/migrate_data.sh db        # database only (schema + data)
#   scripts/migrate_data.sh uploads   # uploads_data volume only (~3.6 GB)
#   scripts/migrate_data.sh all       # db then uploads  (default)
#
# NEW-host SSH settings are read from .env.deploy (already retargeted to EC2).
# OLD-host SSH settings come from the OLD_* env vars below (defaults point at the
# Contabo VPS). Override inline, e.g.:
#   OLD_SSH_KEY=~/.ssh/iilmp_deploy scripts/migrate_data.sh all
#
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
ENV_FILE="${ROOT_DIR}/.env.deploy"

if [ ! -f "$ENV_FILE" ]; then
  echo "error: $ENV_FILE not found." >&2
  exit 1
fi

# shellcheck disable=SC1090
set -a
source "$ENV_FILE"
set +a

# --- NEW host (EC2) — from .env.deploy --------------------------------------
: "${SSH_HOST:?SSH_HOST not set in .env.deploy}"
: "${SSH_USER:?SSH_USER not set in .env.deploy}"
: "${DEPLOY_PATH:?DEPLOY_PATH not set in .env.deploy}"
NEW_SSH_PORT="${SSH_PORT:-22}"
NEW_TARGET="${SSH_USER}@${SSH_HOST}"
NEW_DEPLOY_PATH="$DEPLOY_PATH"

# --- OLD host (current VPS) — overridable via env ---------------------------
OLD_SSH_HOST="${OLD_SSH_HOST:-173.249.17.166}"
OLD_SSH_USER="${OLD_SSH_USER:-root}"
OLD_SSH_PORT="${OLD_SSH_PORT:-22}"
OLD_SSH_KEY="${OLD_SSH_KEY:-$HOME/.ssh/iilmp_deploy}"
OLD_DEPLOY_PATH="${OLD_DEPLOY_PATH:-/var/www/ruforum-iilmp}"
OLD_TARGET="${OLD_SSH_USER}@${OLD_SSH_HOST}"

COMMON_OPTS=(-o StrictHostKeyChecking=accept-new -o ServerAliveInterval=30)

new_ssh() { ssh -p "$NEW_SSH_PORT" "${COMMON_OPTS[@]}" ${SSH_KEY:+-i "$SSH_KEY"} "$NEW_TARGET" "$@"; }
old_ssh() { ssh -p "$OLD_SSH_PORT" "${COMMON_OPTS[@]}" ${OLD_SSH_KEY:+-i "$OLD_SSH_KEY"} "$OLD_TARGET" "$@"; }

COMPOSE="docker compose -f docker-compose.prod.yml"

# Resolve the real uploads volume name on a host (the volume mounted at
# /app/uploads by the web container). Falls back to "<dir>_uploads_data".
RESOLVE_VOL_SNIPPET='
  cd "$1" || exit 1
  cid=$(docker compose -f docker-compose.prod.yml ps -q web 2>/dev/null | head -1)
  name=""
  if [ -n "$cid" ]; then
    name=$(docker inspect "$cid" --format "{{range .Mounts}}{{if eq .Destination \"/app/uploads\"}}{{.Name}}{{end}}{{end}}")
  fi
  if [ -z "$name" ]; then
    proj=$(basename "$1" | tr "[:upper:]" "[:lower:]")
    name="${proj}_uploads_data"
  fi
  printf "%s" "$name"
'

confirm() {
  [ -n "${YES:-}" ] && return 0
  printf "%s [y/N] " "$1"
  read -r ans
  case "$ans" in y|Y|yes|YES) ;; *) echo "Aborted." ; exit 1 ;; esac
}

migrate_db() {
  echo ">> [DB] OLD ${OLD_TARGET} -> NEW ${NEW_TARGET}"
  confirm ">> This OVERWRITES the database on ${SSH_HOST} (--clean) with the OLD prod dump. Continue?"

  echo ">> Stopping app services on NEW (quiet DB for a clean restore)..."
  new_ssh "cd '${NEW_DEPLOY_PATH}' && ${COMPOSE} stop web celery_worker celery_beat"

  echo ">> Streaming pg_dump (OLD) -> psql (NEW)... container reads its own POSTGRES_* creds"
  # pg_dump on OLD straight into psql on NEW. pipefail makes a failure on either
  # end abort. --clean --if-exists drops+recreates objects so it overlays cleanly
  # on the schema redeploy already migrated; --no-owner/--no-acl strip role refs.
  set -o pipefail
  old_ssh "cd '${OLD_DEPLOY_PATH}' && ${COMPOSE} exec -T postgres sh -c 'pg_dump -U \"\$POSTGRES_USER\" -d \"\$POSTGRES_DB\" --no-owner --no-acl --clean --if-exists'" \
    | new_ssh "cd '${NEW_DEPLOY_PATH}' && ${COMPOSE} exec -T postgres sh -c 'psql -v ON_ERROR_STOP=0 -U \"\$POSTGRES_USER\" -d \"\$POSTGRES_DB\"'"

  echo ">> Restarting app services on NEW..."
  new_ssh "cd '${NEW_DEPLOY_PATH}' && ${COMPOSE} up -d web celery_worker celery_beat && ${COMPOSE} restart nginx"
  echo ">> ✓ DB migration complete."
}

migrate_uploads() {
  echo ">> [uploads] resolving volume names..."
  OLD_VOL="${OLD_VOL:-$(old_ssh "bash -s -- '${OLD_DEPLOY_PATH}'" <<EOF
${RESOLVE_VOL_SNIPPET}
EOF
)}"
  NEW_VOL="${NEW_VOL:-$(new_ssh "bash -s -- '${NEW_DEPLOY_PATH}'" <<EOF
${RESOLVE_VOL_SNIPPET}
EOF
)}"
  echo ">>   OLD volume: $OLD_VOL"
  echo ">>   NEW volume: $NEW_VOL"
  confirm ">> Stream ~3.6 GB of media from OLD:$OLD_VOL into NEW:$NEW_VOL? (merges into existing)"

  # tar the volume on OLD to stdout, untar into the volume on NEW. A throwaway
  # alpine container on each side mounts the named volume; nothing is staged on
  # either host's disk.
  set -o pipefail
  old_ssh "docker run --rm -v '${OLD_VOL}:/data:ro' alpine sh -c 'tar czf - -C /data .'" \
    | new_ssh "docker run --rm -i -v '${NEW_VOL}:/data' alpine sh -c 'tar xzf - -C /data'"
  echo ">> ✓ uploads volume migration complete."
}

MODE="${1:-all}"
case "$MODE" in
  db)      migrate_db ;;
  uploads) migrate_uploads ;;
  all)     migrate_db; migrate_uploads ;;
  *)       echo "usage: $0 {db|uploads|all}" >&2; exit 2 ;;
esac

echo
echo ">> Data migration ($MODE) done. Verify on NEW:"
echo ">>   - log in as a known existing user"
echo ">>   - open a repository PDF + a Moodle media asset (confirms uploads_data)"
echo ">>   - make deploy-status   # all services healthy"
echo ">> Do NOT run deploy-migrate-repository/-moodle/-smehub on EC2 — the dump"
echo ">> already contains those imports."
