#!/usr/bin/env bash
# ────────────────────────────────────────────────────────────────────────────────
# E2E Test Orchestrator
#
# Starts all required services, runs Playwright E2E tests, and tears down.
#
# Services managed:
#   1. Gitea instance        (Docker/Podman on port 3333)
#   2. Fake GitHub API       (Node.js on port 4580)
#   3. gitea-mirror app      (Astro dev server on port 4321)
#
# Usage:
#   ./tests/e2e/run-e2e.sh              # full run (cleanup → start → test → teardown)
#   ./tests/e2e/run-e2e.sh --no-build   # skip the Astro build step
#   ./tests/e2e/run-e2e.sh --keep       # don't tear down services after tests
#   ./tests/e2e/run-e2e.sh --ci         # CI-friendly mode (stricter, no --keep)
#
# Environment variables:
#   GITEA_PORT          (default: 3333)
#   FAKE_GITHUB_PORT    (default: 4580)
#   APP_PORT            (default: 4321)
#   SKIP_CLEANUP        (default: false)  set "true" to skip initial cleanup
#   BUN_CMD             (default: auto-detected bun or "npx --yes bun")
# ────────────────────────────────────────────────────────────────────────────────
set -euo pipefail

# ─── Resolve paths ────────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
COMPOSE_FILE="$SCRIPT_DIR/docker-compose.e2e.yml"

# ─── Configuration ────────────────────────────────────────────────────────────
GITEA_PORT="${GITEA_PORT:-3333}"
FAKE_GITHUB_PORT="${FAKE_GITHUB_PORT:-4580}"
APP_PORT="${APP_PORT:-4321}"
GIT_SERVER_PORT="${GIT_SERVER_PORT:-4590}"

GITEA_URL="http://localhost:${GITEA_PORT}"
FAKE_GITHUB_URL="http://localhost:${FAKE_GITHUB_PORT}"
APP_URL="http://localhost:${APP_PORT}"
GIT_SERVER_URL="http://localhost:${GIT_SERVER_PORT}"
# URL that Gitea (inside Docker) uses to reach the git-server container
GIT_SERVER_INTERNAL_URL="http://git-server"

NO_BUILD=false
KEEP_RUNNING=false
CI_MODE=false

for arg in "$@"; do
  case "$arg" in
    --no-build)  NO_BUILD=true ;;
    --keep)      KEEP_RUNNING=true ;;
    --ci)        CI_MODE=true ;;
    --help|-h)
      echo "Usage: $0 [--no-build] [--keep] [--ci]"
      exit 0
      ;;
  esac
done

# ─── Detect tools ─────────────────────────────────────────────────────────────

# Container runtime
COMPOSE_CMD=""
CONTAINER_CMD=""
if command -v podman-compose &>/dev/null; then
  COMPOSE_CMD="podman-compose"
  CONTAINER_CMD="podman"
elif command -v docker-compose &>/dev/null; then
  COMPOSE_CMD="docker-compose"
  CONTAINER_CMD="docker"
elif command -v docker &>/dev/null && docker compose version &>/dev/null 2>&1; then
  COMPOSE_CMD="docker compose"
  CONTAINER_CMD="docker"
else
  echo "ERROR: No container compose tool found. Install docker-compose or podman-compose."
  exit 1
fi

# Bun or fallback
if command -v bun &>/dev/null; then
  BUN_CMD="${BUN_CMD:-bun}"
elif command -v npx &>/dev/null; then
  # Use npx to run bun commands – works on CI with setup-bun action
  BUN_CMD="${BUN_CMD:-npx --yes bun}"
else
  echo "ERROR: Neither bun nor npx found."
  exit 1
fi

# Node/tsx for the fake GitHub server
if command -v tsx &>/dev/null; then
  TSX_CMD="tsx"
elif command -v npx &>/dev/null; then
  TSX_CMD="npx --yes tsx"
else
  echo "ERROR: Neither tsx nor npx found."
  exit 1
fi

echo "╔══════════════════════════════════════════════════════════════╗"
echo "║              E2E Test Orchestrator                           ║"
echo "╠══════════════════════════════════════════════════════════════╣"
echo "║  Container runtime : $COMPOSE_CMD"
echo "║  Bun command       : $BUN_CMD"
echo "║  TSX command       : $TSX_CMD"
echo "║  Gitea URL         : $GITEA_URL"
echo "║  Fake GitHub URL   : $FAKE_GITHUB_URL"
echo "║  App URL           : $APP_URL"
echo "║  Git Server URL    : $GIT_SERVER_URL"
echo "║  Git Server (int)  : $GIT_SERVER_INTERNAL_URL"
echo "║  CI mode           : $CI_MODE"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""

# ─── PID tracking for cleanup ─────────────────────────────────────────────────
FAKE_GITHUB_PID=""
APP_PID=""
EXIT_CODE=0

cleanup_on_exit() {
  local code=$?
  echo ""
  echo "────────────────────────────────────────────────────────────────"
  echo "[teardown] Cleaning up..."

  # Kill fake GitHub server
  if [[ -n "$FAKE_GITHUB_PID" ]] && kill -0 "$FAKE_GITHUB_PID" 2>/dev/null; then
    echo "[teardown] Stopping fake GitHub server (PID $FAKE_GITHUB_PID)..."
    kill "$FAKE_GITHUB_PID" 2>/dev/null || true
    wait "$FAKE_GITHUB_PID" 2>/dev/null || true
  fi
  rm -f "$SCRIPT_DIR/.fake-github.pid"

  # Kill app server
  if [[ -n "$APP_PID" ]] && kill -0 "$APP_PID" 2>/dev/null; then
    echo "[teardown] Stopping gitea-mirror app (PID $APP_PID)..."
    kill "$APP_PID" 2>/dev/null || true
    wait "$APP_PID" 2>/dev/null || true
  fi
  rm -f "$SCRIPT_DIR/.app.pid"

  # Stop containers (unless --keep)
  if [[ "$KEEP_RUNNING" == false ]]; then
    if [[ -n "$COMPOSE_CMD" ]] && [[ -f "$COMPOSE_FILE" ]]; then
      echo "[teardown] Stopping Gitea container..."
      $COMPOSE_CMD -f "$COMPOSE_FILE" down --volumes --remove-orphans 2>/dev/null || true
    fi
  else
    echo "[teardown] --keep flag set, leaving services running"
  fi

  echo "[teardown] Done."

  # Use the test exit code, not the cleanup exit code
  if [[ $EXIT_CODE -ne 0 ]]; then
    exit $EXIT_CODE
  fi
  exit $code
}

trap cleanup_on_exit EXIT INT TERM

# ─── Step 0: Cleanup previous run ────────────────────────────────────────────
if [[ "${SKIP_CLEANUP:-false}" != "true" ]]; then
  echo "┌──────────────────────────────────────────────────────────────┐"
  echo "│  Step 0: Cleanup previous E2E run                            │"
  echo "└──────────────────────────────────────────────────────────────┘"
  bash "$SCRIPT_DIR/cleanup.sh" --soft 2>/dev/null || true
  echo ""
fi

# ─── Step 1: Install dependencies ────────────────────────────────────────────
echo "┌──────────────────────────────────────────────────────────────┐"
echo "│  Step 1: Install dependencies                                │"
echo "└──────────────────────────────────────────────────────────────┘"
cd "$PROJECT_ROOT"
$BUN_CMD install 2>&1 | tail -5
echo "[deps] ✓ Dependencies installed"

# Install Playwright browsers if needed
if ! npx playwright install --dry-run chromium &>/dev/null 2>&1; then
  echo "[deps] Installing Playwright browsers..."
  npx playwright install chromium 2>&1 | tail -3
fi
# Always ensure system deps are available (needed in CI/fresh environments)
if [[ "$CI_MODE" == true ]]; then
  echo "[deps] Installing Playwright system dependencies..."
  npx playwright install-deps chromium 2>&1 | tail -5 || true
fi
echo "[deps] ✓ Playwright ready"
echo ""

# ─── Step 1.5: Create test git repositories ─────────────────────────────────
echo "┌──────────────────────────────────────────────────────────────┐"
echo "│  Step 1.5: Create test git repositories                      │"
echo "└──────────────────────────────────────────────────────────────┘"

GIT_REPOS_DIR="$SCRIPT_DIR/git-repos"
echo "[git-repos] Creating bare git repos in $GIT_REPOS_DIR ..."
$BUN_CMD run "$SCRIPT_DIR/create-test-repos.ts" --output-dir "$GIT_REPOS_DIR" 2>&1

if [[ ! -f "$GIT_REPOS_DIR/manifest.json" ]]; then
  echo "ERROR: Test git repos were not created (manifest.json missing)"
  EXIT_CODE=1
  exit 1
fi

echo "[git-repos] ✓ Test repositories created"
echo ""

# ─── Step 2: Build the app ──────────────────────────────────────────────────
if [[ "$NO_BUILD" == false ]]; then
  echo "┌──────────────────────────────────────────────────────────────┐"
  echo "│  Step 2: Build gitea-mirror                                  │"
  echo "└──────────────────────────────────────────────────────────────┘"
  cd "$PROJECT_ROOT"

  # Initialize the database
  echo "[build] Initializing database..."
  $BUN_CMD run manage-db init 2>&1 | tail -3 || true

  # Build the Astro project
  echo "[build] Building Astro project..."
  GITHUB_API_URL="$FAKE_GITHUB_URL" \
    BETTER_AUTH_SECRET="e2e-test-secret" \
    $BUN_CMD run build 2>&1 | tail -10

  echo "[build] ✓ Build complete"
  echo ""
else
  echo "[build] Skipped (--no-build flag)"
  echo ""
fi

# ─── Step 3: Start Gitea container ──────────────────────────────────────────
echo "┌──────────────────────────────────────────────────────────────┐"
echo "│  Step 3: Start Gitea container                               │"
echo "└──────────────────────────────────────────────────────────────┘"

$COMPOSE_CMD -f "$COMPOSE_FILE" up -d 2>&1

# Wait for git-server to be healthy first (Gitea depends on it)
echo "[git-server] Waiting for git HTTP server..."
GIT_SERVER_READY=false
for i in $(seq 1 30); do
  if curl -sf "${GIT_SERVER_URL}/manifest.json" &>/dev/null; then
    GIT_SERVER_READY=true
    break
  fi
  printf "."
  sleep 1
done
echo ""

if [[ "$GIT_SERVER_READY" != true ]]; then
  echo "ERROR: Git HTTP server did not start within 30 seconds"
  echo "[git-server] Container logs:"
  $COMPOSE_CMD -f "$COMPOSE_FILE" logs git-server --tail=20 2>/dev/null || true
  EXIT_CODE=1
  exit 1
fi
echo "[git-server] ✓ Git HTTP server is ready on $GIT_SERVER_URL"

echo "[gitea] Waiting for Gitea to become healthy..."
GITEA_READY=false
for i in $(seq 1 60); do
  if curl -sf "${GITEA_URL}/api/v1/version" &>/dev/null; then
    GITEA_READY=true
    break
  fi
  printf "."
  sleep 2
done
echo ""

if [[ "$GITEA_READY" != true ]]; then
  echo "ERROR: Gitea did not become healthy within 120 seconds"
  echo "[gitea] Container logs:"
  $COMPOSE_CMD -f "$COMPOSE_FILE" logs gitea-e2e --tail=30 2>/dev/null || true
  EXIT_CODE=1
  exit 1
fi

GITEA_VERSION=$(curl -sf "${GITEA_URL}/api/v1/version" | grep -o '"version":"[^"]*"' | cut -d'"' -f4)
echo "[gitea] ✓ Gitea is ready (version: ${GITEA_VERSION:-unknown})"
echo ""

# ─── Step 4: Start fake GitHub API ──────────────────────────────────────────
echo "┌──────────────────────────────────────────────────────────────┐"
echo "│  Step 4: Start fake GitHub API server                        │"
echo "└──────────────────────────────────────────────────────────────┘"

PORT=$FAKE_GITHUB_PORT GIT_SERVER_URL="$GIT_SERVER_INTERNAL_URL" \
  $TSX_CMD "$SCRIPT_DIR/fake-github-server.ts" &
FAKE_GITHUB_PID=$!
echo "$FAKE_GITHUB_PID" > "$SCRIPT_DIR/.fake-github.pid"

echo "[fake-github] Started (PID: $FAKE_GITHUB_PID)"
echo "[fake-github] Waiting for server to be ready..."

FAKE_READY=false
for i in $(seq 1 30); do
  if curl -sf "${FAKE_GITHUB_URL}/___mgmt/health" &>/dev/null; then
    FAKE_READY=true
    break
  fi
  # Check if process died
  if ! kill -0 "$FAKE_GITHUB_PID" 2>/dev/null; then
    echo "ERROR: Fake GitHub server process died"
    EXIT_CODE=1
    exit 1
  fi
  printf "."
  sleep 1
done
echo ""

if [[ "$FAKE_READY" != true ]]; then
  echo "ERROR: Fake GitHub server did not start within 30 seconds"
  EXIT_CODE=1
  exit 1
fi

echo "[fake-github] ✓ Fake GitHub API is ready on $FAKE_GITHUB_URL"

# Tell the fake GitHub server to use the git-server container URL for clone_url
# (This updates existing repos in the store so Gitea can actually clone them)
echo "[fake-github] Setting clone URL base to $GIT_SERVER_INTERNAL_URL ..."
curl -sf -X POST "${FAKE_GITHUB_URL}/___mgmt/set-clone-url" \
  -H "Content-Type: application/json" \
  -d "{\"url\": \"${GIT_SERVER_INTERNAL_URL}\"}" || true
echo "[fake-github] ✓ Clone URLs configured"
echo ""

# ─── Step 5: Start gitea-mirror app ────────────────────────────────────────
echo "┌──────────────────────────────────────────────────────────────┐"
echo "│  Step 5: Start gitea-mirror application                      │"
echo "└──────────────────────────────────────────────────────────────┘"

cd "$PROJECT_ROOT"

# Reinitialize the database in case build step reset it
$BUN_CMD run manage-db init 2>&1 | tail -2 || true

# Start the app with E2E environment
GITHUB_API_URL="$FAKE_GITHUB_URL" \
  BETTER_AUTH_SECRET="e2e-test-secret" \
  BETTER_AUTH_URL="$APP_URL" \
  DATABASE_URL="file:data/gitea-mirror.db" \
  HOST="0.0.0.0" \
  PORT="$APP_PORT" \
  NODE_ENV="production" \
  PRE_SYNC_BACKUP_ENABLED="false" \
  ENCRYPTION_SECRET="e2e-encryption-secret-32char!!" \
  $BUN_CMD run start &
APP_PID=$!
echo "$APP_PID" > "$SCRIPT_DIR/.app.pid"

echo "[app] Started (PID: $APP_PID)"
echo "[app] Waiting for app to be ready..."

APP_READY=false
for i in $(seq 1 90); do
  # Try the health endpoint first, then fall back to root
  if curl -sf "${APP_URL}/api/health" &>/dev/null 2>&1 || \
     curl -sf -o /dev/null -w "%{http_code}" "${APP_URL}/" 2>/dev/null | grep -q "^[23]"; then
    APP_READY=true
    break
  fi
  # Check if process died
  if ! kill -0 "$APP_PID" 2>/dev/null; then
    echo ""
    echo "ERROR: gitea-mirror app process died"
    EXIT_CODE=1
    exit 1
  fi
  printf "."
  sleep 2
done
echo ""

if [[ "$APP_READY" != true ]]; then
  echo "ERROR: gitea-mirror app did not start within 180 seconds"
  EXIT_CODE=1
  exit 1
fi

echo "[app] ✓ gitea-mirror app is ready on $APP_URL"
echo ""

# ─── Step 6: Run Playwright E2E tests ──────────────────────────────────────
echo "┌──────────────────────────────────────────────────────────────┐"
echo "│  Step 6: Run Playwright E2E tests                            │"
echo "└──────────────────────────────────────────────────────────────┘"

cd "$PROJECT_ROOT"

# Ensure test-results directory exists
mkdir -p "$SCRIPT_DIR/test-results"

# Run Playwright
set +e
APP_URL="$APP_URL" \
  GITEA_URL="$GITEA_URL" \
  FAKE_GITHUB_URL="$FAKE_GITHUB_URL" \
  npx playwright test \
    --config "$SCRIPT_DIR/playwright.config.ts" \
    --reporter=list
PLAYWRIGHT_EXIT=$?
set -e

echo ""

if [[ $PLAYWRIGHT_EXIT -eq 0 ]]; then
  echo "═══════════════════════════════════════════════════════════════"
  echo " ✅ E2E tests PASSED"
  echo "═══════════════════════════════════════════════════════════════"
else
  echo "═══════════════════════════════════════════════════════════════"
  echo " ❌ E2E tests FAILED (exit code: $PLAYWRIGHT_EXIT)"
  echo "═══════════════════════════════════════════════════════════════"

  # On failure, dump some diagnostic info
  echo ""
  echo "[diag] Gitea container status:"
  $COMPOSE_CMD -f "$COMPOSE_FILE" ps 2>/dev/null || true
  echo ""
  echo "[diag] Gitea container logs (last 20 lines):"
  $COMPOSE_CMD -f "$COMPOSE_FILE" logs gitea-e2e --tail=20 2>/dev/null || true
  echo ""
  echo "[diag] Git server logs (last 10 lines):"
  $COMPOSE_CMD -f "$COMPOSE_FILE" logs git-server --tail=10 2>/dev/null || true
  echo ""
  echo "[diag] Git server health:"
  curl -sf "${GIT_SERVER_URL}/manifest.json" 2>/dev/null || echo "(unreachable)"
  echo ""
  echo "[diag] Fake GitHub health:"
  curl -sf "${FAKE_GITHUB_URL}/___mgmt/health" 2>/dev/null || echo "(unreachable)"
  echo ""
  echo "[diag] App health:"
  curl -sf "${APP_URL}/api/health" 2>/dev/null || echo "(unreachable)"
  echo ""

  # Point to HTML report
  if [[ -d "$SCRIPT_DIR/playwright-report" ]]; then
    echo "[diag] HTML report: $SCRIPT_DIR/playwright-report/index.html"
    echo "       Run: npx playwright show-report $SCRIPT_DIR/playwright-report"
  fi

  EXIT_CODE=$PLAYWRIGHT_EXIT
fi

# EXIT_CODE is used by the trap handler
exit $EXIT_CODE
