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

# qwickapps-migrate: Dev migration helper for @qwickapps/cms
#
# Default mode: Auto-migrate on dev start
#   - Syncs committed migrations to .dev-migrations/
#   - Generates a new migration if schema changed
#   - Applies all pending migrations
#
# Promote mode (--promote):
#   - Copies new .dev-migrations/ files to src/migrations/
#   - Regenerates src/migrations/index.ts
#
# Flags:
#   --promote    Promote dev migrations to src/migrations/
#   --dry-run    Print what would happen without executing

# ---------------------------------------------------------------------------
# Color output helpers
# ---------------------------------------------------------------------------
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
RESET='\033[0m'

log_info()  { printf "${GREEN}[migrate]${RESET} %s\n" "$*"; }
log_warn()  { printf "${YELLOW}[migrate] WARN:${RESET} %s\n" "$*"; }
log_error() { printf "${RED}[migrate] ERROR:${RESET} %s\n" "$*" >&2; }

# ---------------------------------------------------------------------------
# Argument parsing
# ---------------------------------------------------------------------------
MODE="auto"
DRY_RUN=false

for arg in "$@"; do
  case "$arg" in
    --promote)  MODE="promote" ;;
    --dry-run)  DRY_RUN=true ;;
    *)
      log_error "Unknown flag: $arg"
      exit 1
      ;;
  esac
done

# ---------------------------------------------------------------------------
# Resolve paths (script is called from the project root, e.g. clients/faabzi)
# ---------------------------------------------------------------------------
PROJECT_ROOT="$(pwd)"
SRC_MIGRATIONS="${PROJECT_ROOT}/src/migrations"
DEV_MIGRATIONS="${PROJECT_ROOT}/.dev-migrations"

# ---------------------------------------------------------------------------
# Utility: count .ts files in a directory, excluding index.ts
# ---------------------------------------------------------------------------
count_ts_files() {
  local dir="$1"
  find "$dir" -maxdepth 1 -name "*.ts" ! -name "index.ts" | wc -l | tr -d ' '
}

# ---------------------------------------------------------------------------
# Utility: regenerate src/migrations/index.ts
# Scans all .ts files (excluding index.ts), sorted, and writes the index.
# ---------------------------------------------------------------------------
regenerate_index() {
  local migrations_dir="$1"
  local index_file="${migrations_dir}/index.ts"

  # Collect migration basenames, sorted
  local names=()
  while IFS= read -r -d '' f; do
    names+=("$(basename "$f" .ts)")
  done < <(find "$migrations_dir" -maxdepth 1 -name "*.ts" ! -name "index.ts" -print0 | sort -z)

  if [ "${#names[@]}" -eq 0 ]; then
    log_warn "No migration files found in ${migrations_dir}; index.ts will be empty."
  fi

  if [ "$DRY_RUN" = true ]; then
    log_info "[dry-run] Would regenerate ${index_file} with ${#names[@]} migration(s)."
    return
  fi

  {
    for name in "${names[@]}"; do
      printf "import * as migration_%s from './%s';\n" "$name" "$name"
    done

    printf "\nexport const migrations = [\n"
    local last_idx=$(( ${#names[@]} - 1 ))
    for i in "${!names[@]}"; do
      local name="${names[$i]}"
      # Omit trailing comma on the last entry to match existing format
      if [ "$i" -lt "$last_idx" ]; then
        printf "  {\n    up: migration_%s.up,\n    down: migration_%s.down,\n    name: '%s',\n  },\n" \
          "$name" "$name" "$name"
      else
        printf "  {\n    up: migration_%s.up,\n    down: migration_%s.down,\n    name: '%s'\n  },\n" \
          "$name" "$name" "$name"
      fi
    done
    printf "];\n"
  } > "$index_file"

  log_info "Regenerated ${index_file} (${#names[@]} migration(s))."
}

# ---------------------------------------------------------------------------
# MODE: promote
# ---------------------------------------------------------------------------
if [ "$MODE" = "promote" ]; then
  if [ ! -d "$DEV_MIGRATIONS" ]; then
    log_error ".dev-migrations/ does not exist. Nothing to promote."
    exit 1
  fi

  if [ ! -d "$SRC_MIGRATIONS" ]; then
    log_error "src/migrations/ does not exist. Cannot promote."
    exit 1
  fi

  promoted=()

  while IFS= read -r -d '' f; do
    filename="$(basename "$f")"

    # Skip index.ts - it will be regenerated
    if [ "$filename" = "index.ts" ]; then
      continue
    fi

    dest="${SRC_MIGRATIONS}/${filename}"
    if [ -e "$dest" ]; then
      # Already promoted - skip silently
      continue
    fi

    promoted+=("$filename")

    if [ "$DRY_RUN" = true ]; then
      log_info "[dry-run] Would promote: ${filename}"
    else
      cp "$f" "$dest"
      log_info "Promoted: ${filename}"
    fi
  done < <(find "$DEV_MIGRATIONS" -maxdepth 1 \( -name "*.ts" -o -name "*.json" \) -print0 | sort -z)

  if [ "${#promoted[@]}" -eq 0 ]; then
    log_info "No new files to promote. src/migrations/ is already up to date."
    exit 0
  fi

  # Regenerate index.ts after promotion
  if [ "$DRY_RUN" = true ]; then
    log_info "[dry-run] Would regenerate src/migrations/index.ts."
  else
    regenerate_index "$SRC_MIGRATIONS"
  fi

  log_info "Promote complete. ${#promoted[@]} file(s) moved to src/migrations/."
  exit 0
fi

# ---------------------------------------------------------------------------
# MODE: auto (default - dev startup)
# ---------------------------------------------------------------------------

# Step 1: Create .dev-migrations/ if it does not exist
if [ "$DRY_RUN" = true ]; then
  log_info "[dry-run] Would create ${DEV_MIGRATIONS} if missing."
else
  mkdir -p "$DEV_MIGRATIONS"
fi

# Step 2: Sync committed migrations to .dev-migrations/
# Copy .ts and .json files that don't already exist (preserves dev-auto files
# and index.ts that Payload's migrate:create manages).
if [ -d "$SRC_MIGRATIONS" ]; then
  if [ "$DRY_RUN" = true ]; then
    log_info "[dry-run] Would sync new files from ${SRC_MIGRATIONS}/ -> ${DEV_MIGRATIONS}/"
  else
    synced=0
    for file in "$SRC_MIGRATIONS"/*.ts "$SRC_MIGRATIONS"/*.json; do
      [ -f "$file" ] || continue
      dest="${DEV_MIGRATIONS}/$(basename "$file")"
      if [ ! -f "$dest" ]; then
        cp "$file" "$dest"
        synced=$((synced + 1))
      fi
    done
    if [ "$synced" -gt 0 ]; then
      log_info "Synced ${synced} new committed migration(s) to .dev-migrations/"
    else
      log_info "Committed migrations already synced."
    fi
  fi
else
  log_warn "src/migrations/ not found. Skipping sync step."
fi

# Step 3-6: Generate and apply migrations
if [ "$DRY_RUN" = true ]; then
  log_info "[dry-run] Would run: DEV_MIGRATION_DIR=${DEV_MIGRATIONS} pnpm exec payload migrate:create dev-auto"
  log_info "[dry-run] Would run: DEV_MIGRATION_DIR=${DEV_MIGRATIONS} pnpm exec payload migrate"
else
  # Count .ts files before generation (excluding index.ts)
  count_before="$(count_ts_files "$DEV_MIGRATIONS")"

  # Generate a migration if schema changed
  log_info "Checking for schema changes..."
  if ! DEV_MIGRATION_DIR="${DEV_MIGRATIONS}" pnpm exec payload migrate:create dev-auto 2>&1; then
    log_error "Migration generation failed."
    exit 1
  fi

  # Count .ts files after generation
  count_after="$(count_ts_files "$DEV_MIGRATIONS")"

  if [ "$count_after" -gt "$count_before" ]; then
    # Check if the new migration is empty (Payload creates files even with no changes).
    # An empty migration has no db.execute call in the up function.
    # Find the latest dev_auto file by sorted name (works on macOS and Linux).
    new_migration="$(find "$DEV_MIGRATIONS" -maxdepth 1 -name "*_dev_auto.ts" | sort | tail -n 1)"

    if [ -n "$new_migration" ] && ! grep -q 'db\.execute' "$new_migration"; then
      # Empty migration - remove it and its JSON snapshot
      log_info "No schema changes detected (empty migration removed)."
      rm -f "$new_migration"
      rm -f "${new_migration%.ts}.json"
    else
      log_info "Schema changes detected. New migration file(s) created in .dev-migrations/."
    fi
  else
    log_info "No schema changes detected."
  fi

  # Apply pending migrations
  log_info "Applying pending migrations..."
  if ! DEV_MIGRATION_DIR="${DEV_MIGRATIONS}" pnpm exec payload migrate 2>&1; then
    log_error "Migration apply failed."
    exit 1
  fi
fi

log_info "Migrations complete."
exit 0
