#!/usr/bin/env bash
# ────────────────────────────────────────────────────────────────────────────
# CMS Data Sync Tool
# Safely syncs Payload CMS data (DB + media) between environments.
#
# Usage:
#   cms:push:testing   – Push local → testing
#   cms:push:prod      – Push local → production  
#   cms:pull:testing   – Pull testing → local
#   cms:pull:prod      – Pull production → local
# ────────────────────────────────────────────────────────────────────────────
set -euo pipefail

SYNC_SUCCESS="false"
LOCAL_BACKUP_FILE=""
REMOTE_BACKUP_FILE=""

cleanup_on_exit() {
  local exit_code=$?
  if [ "$SYNC_SUCCESS" != "true" ] && [ $exit_code -ne 0 ]; then
    echo ""
    echo "❌ Sync aborted or failed! (Exit code: $exit_code)"
    if [ "${DIRECTION:-}" = "push" ] && [ -n "${REMOTE_BACKUP_FILE:-}" ]; then
      echo "🔄 Rolling back $TARGET database..."
      ssh "$SSH_HOST" "gunzip -c $REMOTE_BACKUP_FILE | docker exec -i $REMOTE_DB_CONTAINER psql -U $REMOTE_DB_USER -d $REMOTE_DB_NAME --quiet" || echo "⚠️ Rollback failed"
      echo "✅ Rollback complete."
    elif [ "${DIRECTION:-}" = "pull" ] && [ -n "${LOCAL_BACKUP_FILE:-}" ]; then
      echo "🔄 Rolling back local database..."
      gunzip -c "$LOCAL_BACKUP_FILE" | docker exec -i "$LOCAL_DB_CONTAINER" psql -U "$LOCAL_DB_USER" -d "$LOCAL_DB_NAME" --quiet || echo "⚠️ Rollback failed"
      echo "✅ Rollback complete."
    fi
  fi
}
trap 'cleanup_on_exit' EXIT

# Load environment variables
if [ -f .env ]; then
  set -a; source .env; set +a
fi

# ── Configuration ──────────────────────────────────────────────────────────
DIRECTION="${1:-}"         # push | pull
TARGET="${2:-}"            # testing | prod
SSH_HOST="root@alpha.mintel.me"
LOCAL_DB_USER="${PAYLOAD_DB_USER:-payload}"
LOCAL_DB_NAME="${PAYLOAD_DB_NAME:-payload}"
LOCAL_DB_CONTAINER="klz-2026-klz-db-1"
LOCAL_MEDIA_DIR="./public/media"
BACKUP_DIR="./backups"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")

# Remote credentials (resolved per-target from server env files)
REMOTE_DB_USER=""
REMOTE_DB_NAME=""

# Auto-detect migrations from src/migrations/*.ts (no manual maintenance needed)
MIGRATIONS=()
BATCH=1
for migration_file in $(ls src/migrations/*.ts 2>/dev/null | sort); do
  name=$(basename "$migration_file" .ts)
  MIGRATIONS+=("$name:$BATCH")
  ((BATCH++))
done
if [ ${#MIGRATIONS[@]} -eq 0 ]; then
  echo "⚠️  No migration files found in src/migrations/"
fi

# ── Resolve target environment ─────────────────────────────────────────────
resolve_target() {
  case "$TARGET" in
    testing)
      REMOTE_PROJECT="klz-testing"
      REMOTE_DB_CONTAINER="klz-testing-klz-db-1"
      REMOTE_APP_CONTAINER="klz-testing-klz-app-1"
      REMOTE_MEDIA_VOLUME="/var/lib/docker/volumes/klz-testing_klz_media_data/_data"
      REMOTE_SITE_DIR="/home/deploy/sites/testing.klz-cables.com"
      ;;
    staging)
      REMOTE_PROJECT="klz-staging"
      REMOTE_DB_CONTAINER="klz-staging-klz-db-1"
      REMOTE_APP_CONTAINER="klz-staging-klz-app-1"
      REMOTE_MEDIA_VOLUME="/var/lib/docker/volumes/klz-staging_klz_media_data/_data"
      REMOTE_SITE_DIR="/home/deploy/sites/staging.klz-cables.com"
      ;;
    prod|production)
      REMOTE_PROJECT="klz-cablescom"
      REMOTE_DB_CONTAINER="klz-cablescom-klz-db-1"
      REMOTE_APP_CONTAINER="klz-cablescom-klz-app-1"
      REMOTE_MEDIA_VOLUME="/var/lib/docker/volumes/klz-cablescom_klz_media_data/_data"
      REMOTE_SITE_DIR="/home/deploy/sites/klz-cables.com"
      ;;
    *)
      echo "❌ Unknown target: $TARGET"
      echo "   Valid targets: testing, staging, prod"
      exit 1
      ;;
  esac

  # Auto-detect remote DB credentials from the env file on the server
  echo "🔍 Detecting $TARGET database credentials..."
  REMOTE_DB_USER=$(ssh "$SSH_HOST" "grep -h '^PAYLOAD_DB_USER=' $REMOTE_SITE_DIR/.env* 2>/dev/null | tail -1 | cut -d= -f2" || echo "")
  REMOTE_DB_NAME=$(ssh "$SSH_HOST" "grep -h '^PAYLOAD_DB_NAME=' $REMOTE_SITE_DIR/.env* 2>/dev/null | tail -1 | cut -d= -f2" || echo "")
  REMOTE_DB_USER="${REMOTE_DB_USER:-payload}"
  REMOTE_DB_NAME="${REMOTE_DB_NAME:-payload}"
  echo "   User: $REMOTE_DB_USER | DB: $REMOTE_DB_NAME"
}

# ── Ensure local DB is running ─────────────────────────────────────────────
ensure_local_db() {
  if ! docker ps --format '{{.Names}}' | grep -q "$LOCAL_DB_CONTAINER"; then
    echo "⏳ Local DB container not running. Starting..."
    docker compose up -d klz-db
    echo "⏳ Waiting for local DB to be ready..."
    for i in $(seq 1 10); do
      if docker exec "$LOCAL_DB_CONTAINER" pg_isready -U "$LOCAL_DB_USER" -q 2>/dev/null; then
        echo "✅ Local DB is ready."
        return
      fi
      sleep 1
    done
    echo "❌ Local DB failed to start."
    exit 1
  fi
}

# ── Sanitize migrations table ──────────────────────────────────────────────
sanitize_migrations() {
  local container="$1"
  local db_user="$2"
  local db_name="$3"
  local is_remote="$4"  # "true" or "false"

  echo "🔧 Sanitizing payload_migrations table..."
  local SQL="DELETE FROM payload_migrations WHERE batch = -1;"
  for entry in "${MIGRATIONS[@]}"; do
    local name="${entry%%:*}"
    local batch="${entry##*:}"
    SQL="$SQL INSERT INTO payload_migrations (name, batch) SELECT '$name', $batch WHERE NOT EXISTS (SELECT 1 FROM payload_migrations WHERE name = '$name');"
  done

  if [ "$is_remote" = "true" ]; then
    ssh "$SSH_HOST" "docker exec $container psql -U $db_user -d $db_name -c \"$SQL\""
  else
    docker exec "$container" psql -U "$db_user" -d "$db_name" -c "$SQL"
  fi
}

# ── Safety: Create backup before overwriting ───────────────────────────────
backup_local_db() {
  mkdir -p "$BACKUP_DIR"
  local file="$BACKUP_DIR/payload_pre_sync_${TIMESTAMP}.sql.gz"
  echo "📦 Creating safety backup of local DB → $file"
  docker exec "$LOCAL_DB_CONTAINER" pg_dump -U "$LOCAL_DB_USER" -d "$LOCAL_DB_NAME" --clean --if-exists | gzip > "$file"
  echo "✅ Backup: $file ($(du -h "$file" | cut -f1))"
  LOCAL_BACKUP_FILE="$file"
}

backup_remote_db() {
  local file="/tmp/payload_pre_sync_${TIMESTAMP}.sql.gz"
  echo "📦 Creating safety backup of $TARGET DB → $SSH_HOST:$file"
  ssh "$SSH_HOST" "docker exec $REMOTE_DB_CONTAINER pg_dump -U $REMOTE_DB_USER -d $REMOTE_DB_NAME --clean --if-exists | gzip > $file"
  echo "✅ Remote backup: $file"
  REMOTE_BACKUP_FILE="$file"
}

# ── Pre-flight: Verify remote containers exist ─────────────────────────────
check_remote_containers() {
  echo "🔍 Checking $TARGET containers..."
  local missing=0
  if ! ssh "$SSH_HOST" "docker ps -q -f name=$REMOTE_DB_CONTAINER" | grep -q .; then
    echo "❌ Database container '$REMOTE_DB_CONTAINER' not found on $SSH_HOST"
    echo "   → Deploy $TARGET first: git push to trigger pipeline, or run:"
    echo "     ssh $SSH_HOST \"cd $REMOTE_SITE_DIR && docker compose -p $REMOTE_PROJECT --env-file .env.\$TARGET up -d\""
    missing=1
  fi
  if ! ssh "$SSH_HOST" "docker ps -q -f name=$REMOTE_APP_CONTAINER" | grep -q .; then
    echo "❌ App container '$REMOTE_APP_CONTAINER' not found on $SSH_HOST"
    missing=1
  fi
  if [ $missing -eq 1 ]; then
    echo ""
    echo "💡 The $TARGET environment hasn't been deployed yet."
    echo "   Push to the '$TARGET' branch or run the pipeline first."
    exit 1
  fi
  echo "✅ All $TARGET containers running."
}

# ── PUSH: local → remote ──────────────────────────────────────────────────
do_push() {
  echo ""
  echo "┌──────────────────────────────────────────────────┐"
  echo "│  📤 PUSH: local → $TARGET                       "
  echo "│  This will OVERWRITE the $TARGET database!       "
  echo "│  A safety backup will be created first.          "
  echo "└──────────────────────────────────────────────────┘"
  echo ""
  read -p "Are you sure? (y/N) " -n 1 -r
  echo ""
  [[ ! $REPLY =~ ^[Yy]$ ]] && { echo "Cancelled."; exit 0; }

  # 0. Ensure local DB is running & remote containers exist
  ensure_local_db
  check_remote_containers

  # 1. Safety backup of remote
  backup_remote_db

  # 2. Dump local DB
  echo "📤 Dumping local database..."
  local dump="/tmp/payload_push_${TIMESTAMP}.sql.gz"
  docker exec "$LOCAL_DB_CONTAINER" pg_dump -U "$LOCAL_DB_USER" -d "$LOCAL_DB_NAME" --clean --if-exists | gzip > "$dump"

  # 3. Transfer and restore
  echo "📤 Transferring to $SSH_HOST..."
  scp "$dump" "$SSH_HOST:/tmp/payload_push.sql.gz"
  
  echo "🔄 Restoring database on $TARGET..."
  ssh "$SSH_HOST" "gunzip -c /tmp/payload_push.sql.gz | docker exec -i $REMOTE_DB_CONTAINER psql -U $REMOTE_DB_USER -d $REMOTE_DB_NAME --quiet"

  # 4. Sanitize migrations
  sanitize_migrations "$REMOTE_DB_CONTAINER" "$REMOTE_DB_USER" "$REMOTE_DB_NAME" "true"

  # 5. Sync media
  echo "🖼️  Syncing media files..."
  rsync -az --delete --progress "$LOCAL_MEDIA_DIR/" "$SSH_HOST:$REMOTE_MEDIA_VOLUME/"

  # Fix ownership: rsync preserves local UID, but container runs as nextjs (1001)
  echo "🔑 Fixing media file permissions..."
  ssh "$SSH_HOST" "docker exec -u 0 $REMOTE_APP_CONTAINER chown -R 1001:65533 /app/public/media/ 2>/dev/null || true"

  # 6. Restart app
  echo "🔄 Restarting $TARGET app container..."
  ssh "$SSH_HOST" "docker restart $REMOTE_APP_CONTAINER"

  # Cleanup
  rm -f "$dump"
  ssh "$SSH_HOST" "rm -f /tmp/payload_push.sql.gz"

  SYNC_SUCCESS="true"
  echo ""
  echo "✅ Push to $TARGET complete!"
}

# ── PULL: remote → local ──────────────────────────────────────────────────
do_pull() {
  echo ""
  echo "┌──────────────────────────────────────────────────┐"
  echo "│  📥 PULL: $TARGET → local                       "
  echo "│  This will OVERWRITE your local database!        "
  echo "│  A safety backup will be created first.          "
  echo "└──────────────────────────────────────────────────┘"
  echo ""
  read -p "Are you sure? (y/N) " -n 1 -r
  echo ""
  [[ ! $REPLY =~ ^[Yy]$ ]] && { echo "Cancelled."; exit 0; }

  # 0. Ensure local DB is running & remote containers exist
  ensure_local_db
  check_remote_containers

  # 1. Safety backup of local
  backup_local_db

  # 2. Dump remote DB
  echo "📥 Dumping $TARGET database..."
  ssh "$SSH_HOST" "docker exec $REMOTE_DB_CONTAINER pg_dump -U $REMOTE_DB_USER -d $REMOTE_DB_NAME --clean --if-exists | gzip > /tmp/payload_pull.sql.gz"

  # 3. Transfer and restore
  echo "📥 Downloading from $SSH_HOST..."
  scp "$SSH_HOST:/tmp/payload_pull.sql.gz" "/tmp/payload_pull.sql.gz"
  
  echo "🔄 Restoring database locally..."
  gunzip -c "/tmp/payload_pull.sql.gz" | docker exec -i "$LOCAL_DB_CONTAINER" psql -U "$LOCAL_DB_USER" -d "$LOCAL_DB_NAME" --quiet

  # 4. Sync media
  echo "🖼️  Syncing media files..."
  mkdir -p "$LOCAL_MEDIA_DIR"
  rsync -az --delete --progress "$SSH_HOST:$REMOTE_MEDIA_VOLUME/" "$LOCAL_MEDIA_DIR/"

  # Cleanup
  rm -f "/tmp/payload_pull.sql.gz"
  ssh "$SSH_HOST" "rm -f /tmp/payload_pull.sql.gz"

  SYNC_SUCCESS="true"
  echo ""
  echo "✅ Pull from $TARGET complete! Restart dev server to see changes."
}

# ── Main ───────────────────────────────────────────────────────────────────
if [ -z "$DIRECTION" ] || [ -z "$TARGET" ]; then
  echo "📦 CMS Data Sync Tool"
  echo ""
  echo "Usage:"  
  echo "  pnpm cms:push:testing    Push local DB + media → testing"
  echo "  pnpm cms:push:staging    Push local DB + media → staging"
  echo "  pnpm cms:push:prod       Push local DB + media → production"
  echo "  pnpm cms:pull:testing    Pull testing DB + media → local"
  echo "  pnpm cms:pull:staging    Pull staging DB + media → local"
  echo "  pnpm cms:pull:prod       Pull production DB + media → local"
  echo ""
  echo "Safety: A backup is always created before overwriting."
  exit 1
fi

resolve_target

case "$DIRECTION" in
  push) do_push ;;
  pull) do_pull ;;
  *)
    echo "❌ Unknown direction: $DIRECTION (use 'push' or 'pull')"
    exit 1
    ;;
esac
