#!/usr/bin/env bash
#
# Parallel E2E Test Orchestrator
#
# Discovers and runs multiple Playwright E2E test suites in parallel across different features.
# Each feature gets its own Docker Compose stack and Playwright instance.
#
# Usage:
#   ./run-e2e-parallel                              # Run all E2E suites
#   ./run-e2e-parallel marketplace platform-admin   # Run specific features
#   ./run-e2e-parallel marketplace/*                # Pattern matching
#   ./run-e2e-parallel --workers=8                  # Pass workers to each Playwright instance
#   ./run-e2e-parallel --sequential                 # Run suites sequentially (not parallel)
#   ./run-e2e-parallel --dry-run                    # Discover suites without running
#
# Environment Variables:
#   E2E_PARALLEL_MODE        Run suites in parallel or sequential (default: parallel)
#   WORKERS                  Number of Playwright workers per suite (default: 4)
#   E2E_BUILD                Rebuild Docker images before starting (default: false)
#   E2E_TEARDOWN             Teardown containers after tests (default: true)
#   E2E_VERBOSE              Verbose output (default: false)
#   E2E_AUTO_START_SERVICES  Auto-start required backend services (default: true)
#
# Examples:
#   ./run-e2e-parallel marketplace platform-admin   # Run 2 suites in parallel
#   WORKERS=8 ./run-e2e-parallel                    # 8 workers per suite
#   ./run-e2e-parallel --build                      # Rebuild Docker images
#   E2E_BUILD=true ./run-e2e-parallel              # Same as --build
#   E2E_TEARDOWN=false ./run-e2e-parallel          # Leave containers running
#   ./run-e2e-parallel --sequential marketplace    # Sequential mode

set -euo pipefail

# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
FEATURES_DIR="$SCRIPT_DIR/features"
PARALLEL_MODE="${E2E_PARALLEL_MODE:-parallel}"
WORKERS="${WORKERS:-4}"
TEARDOWN="${E2E_TEARDOWN:-true}"
VERBOSE="${E2E_VERBOSE:-false}"
BUILD="${E2E_BUILD:-false}"
DRY_RUN=false
AUTO_START_SERVICES="${E2E_AUTO_START_SERVICES:-true}"
PLAYWRIGHT_ARGS=()
FEATURE_PATTERNS=()
STARTED_SERVICES=()

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
RESET='\033[0m'

# Parse arguments
while [[ $# -gt 0 ]]; do
  case "$1" in
    --sequential)
      PARALLEL_MODE="sequential"
      shift
      ;;
    --workers=*)
      WORKERS="${1#*=}"
      shift
      ;;
    --no-teardown)
      TEARDOWN="false"
      shift
      ;;
    --verbose)
      VERBOSE="true"
      shift
      ;;
    --build)
      BUILD="true"
      shift
      ;;
    --no-auto-start)
      AUTO_START_SERVICES="false"
      shift
      ;;
    --dry-run)
      DRY_RUN=true
      shift
      ;;
    --help)
      sed -n '2,24p' "$0" | sed 's/^# //; s/^#//'
      exit 0
      ;;
    --*)
      # Pass through to Playwright
      PLAYWRIGHT_ARGS+=("$1")
      shift
      ;;
    *)
      # Feature pattern
      FEATURE_PATTERNS+=("$1")
      shift
      ;;
  esac
done

# Logging
log() {
  echo -e "${BLUE}[E2E]${RESET} $*"
}

log_success() {
  echo -e "${GREEN}[E2E]${RESET} $*"
}

log_error() {
  echo -e "${RED}[E2E]${RESET} $*" >&2
}

log_warn() {
  echo -e "${YELLOW}[E2E]${RESET} $*"
}

log_suite() {
  local suite="$1"
  shift
  echo -e "${MAGENTA}[${suite}]${RESET} $*"
}

verbose() {
  if [ "$VERBOSE" = "true" ]; then
    echo -e "${CYAN}[DEBUG]${RESET} $*" >&2
  fi
}

# Discover E2E test suites
discover_suites() {
  local -a suites=()

  # Find all e2e directories with docker-compose and run scripts
  while IFS= read -r -d '' e2e_dir; do
    local feature_path="${e2e_dir#$FEATURES_DIR/}"
    feature_path="${feature_path%/e2e}"

    # Check if docker-compose file exists
    local compose_file=""
    if [ -f "$e2e_dir/docker-compose.yml" ]; then
      compose_file="$e2e_dir/docker-compose.yml"
    elif [ -f "$e2e_dir/docker-compose.e2e.yml" ]; then
      compose_file="$e2e_dir/docker-compose.e2e.yml"
    else
      verbose "Skipping $feature_path - no docker-compose file"
      continue
    fi

    # Check if run script exists
    if [ ! -x "$e2e_dir/run" ]; then
      verbose "Skipping $feature_path - no executable run script"
      continue
    fi

    # Apply feature patterns if specified
    if [ ${#FEATURE_PATTERNS[@]} -gt 0 ]; then
      local match=false
      for pattern in "${FEATURE_PATTERNS[@]}"; do
        if [[ "$feature_path" == $pattern ]]; then
          match=true
          break
        fi
      done
      if [ "$match" = "false" ]; then
        verbose "Skipping $feature_path - doesn't match patterns"
        continue
      fi
    fi

    suites+=("$feature_path|$e2e_dir|$compose_file")
  done < <(find "$FEATURES_DIR" -type d -name "e2e" -print0)

  printf '%s\n' "${suites[@]}"
}

# Extract feature name from suite path (e.g., "marketplace/frontend-public" → "marketplace")
get_feature_from_suite() {
  local suite_path="$1"
  echo "${suite_path%%/*}"
}

# Check if a backend service is running via health check
check_service_health() {
  local feature="$1"
  local port="$2"
  local health_path="${3:-/health}"

  if curl -sf --connect-timeout 2 "http://localhost:${port}${health_path}" &>/dev/null; then
    return 0
  fi
  return 1
}

# Start backend services for a feature if not already running
start_backend_services() {
  local feature="$1"

  log "Checking backend services for $feature..."

  # Determine required port based on feature
  local api_port=""
  case "$feature" in
    marketplace)
      api_port="3001"
      ;;
    platform-admin)
      api_port="3011"
      ;;
    *)
      verbose "Unknown feature $feature - skipping backend startup"
      return 0
      ;;
  esac

  # Check if service is already running
  if check_service_health "$feature" "$api_port"; then
    log_success "$feature API already running on port $api_port"
    return 0
  fi

  log "Starting $feature backend services..."

  # Start using pnpm dev:start (runs in background with PID tracking)
  cd "$PROJECT_ROOT"
  if [ "$VERBOSE" = "true" ]; then
    pnpm dev:start "$feature" &
    local start_pid=$!
    wait "$start_pid"
  else
    pnpm dev:start "$feature" &>/dev/null &
    local start_pid=$!
    wait "$start_pid"
  fi

  if [ $? -ne 0 ]; then
    log_error "Failed to start $feature backend"
    return 1
  fi

  # Track that we started this service
  STARTED_SERVICES+=("$feature")

  # Wait for service to be healthy (max 60s)
  log "Waiting for $feature API to be healthy..."
  local max_wait=60
  local elapsed=0

  while [ $elapsed -lt $max_wait ]; do
    if check_service_health "$feature" "$api_port"; then
      log_success "$feature API is healthy (port $api_port)"
      return 0
    fi
    sleep 2
    elapsed=$((elapsed + 2))
  done

  log_error "$feature API did not become healthy after ${max_wait}s"
  return 1
}

# Teardown backend services we started
teardown_backend_services() {
  if [ ${#STARTED_SERVICES[@]} -eq 0 ]; then
    return 0
  fi

  log "Stopping backend services we started..."

  cd "$PROJECT_ROOT"
  if pnpm dev:start --stop &>/dev/null; then
    log_success "Backend services stopped"
  else
    log_warn "Failed to stop some backend services"
  fi
}

# Setup Docker Compose for a suite
setup_suite() {
  local feature="$1"
  local e2e_dir="$2"
  local compose_file="$3"

  log_suite "$feature" "Setting up Docker Compose..."

  cd "$e2e_dir"

  # Create test-results directory with proper permissions
  # Docker volumes need host directories to exist before mounting
  if [ -d "test-results" ]; then
    # Clean existing results to avoid stale data
    rm -rf test-results/*
  else
    mkdir -p test-results
  fi
  # Set permissive permissions so container can write (will be owned by container user)
  chmod 777 test-results

  # Build and start services
  local build_flag=""
  if [ "$BUILD" = "true" ]; then
    build_flag="--build"
  fi

  if [ "$VERBOSE" = "true" ]; then
    docker compose -f "$(basename "$compose_file")" up -d $build_flag
  else
    docker compose -f "$(basename "$compose_file")" up -d $build_flag >/dev/null 2>&1
  fi

  local exit_code=$?
  if [ $exit_code -ne 0 ]; then
    log_error "Failed to start Docker Compose for $feature"
    return $exit_code
  fi

  # Wait for services to be healthy
  log_suite "$feature" "Waiting for services to be healthy..."
  local max_wait=60
  local elapsed=0

  while [ $elapsed -lt $max_wait ]; do
    local unhealthy=$(docker compose -f "$(basename "$compose_file")" ps --filter "health=unhealthy" -q | wc -l)
    local starting=$(docker compose -f "$(basename "$compose_file")" ps --filter "health=starting" -q | wc -l)

    if [ "$unhealthy" -eq 0 ] && [ "$starting" -eq 0 ]; then
      log_suite "$feature" "All services healthy"
      return 0
    fi

    sleep 2
    elapsed=$((elapsed + 2))
  done

  log_warn "Services for $feature may not be fully healthy after ${max_wait}s"
  docker compose -f "$(basename "$compose_file")" ps
  return 0
}

# Run tests for a suite
run_suite() {
  local feature="$1"
  local e2e_dir="$2"

  log_suite "$feature" "Running tests with $WORKERS workers..."

  cd "$e2e_dir"

  # Build command
  local cmd=(./run --workers="$WORKERS")
  cmd+=("${PLAYWRIGHT_ARGS[@]}")

  # Run tests
  if [ "$VERBOSE" = "true" ]; then
    WORKERS="$WORKERS" "${cmd[@]}"
  else
    # Use awk instead of sed to avoid delimiter issues with feature names containing slashes
    WORKERS="$WORKERS" "${cmd[@]}" 2>&1 | awk -v prefix="[$feature] " '{print prefix $0}'
  fi

  local exit_code=$?

  if [ $exit_code -eq 0 ]; then
    log_suite "$feature" "${GREEN}✓ Tests passed${RESET}"
  else
    log_suite "$feature" "${RED}✗ Tests failed (exit $exit_code)${RESET}"
  fi

  return $exit_code
}

# Teardown Docker Compose for a suite
teardown_suite() {
  local feature="$1"
  local e2e_dir="$2"
  local compose_file="$3"

  if [ "$TEARDOWN" != "true" ]; then
    log_suite "$feature" "Skipping teardown (E2E_TEARDOWN=false)"
    return 0
  fi

  log_suite "$feature" "Tearing down Docker Compose..."

  cd "$e2e_dir"

  if [ "$VERBOSE" = "true" ]; then
    docker compose -f "$(basename "$compose_file")" down -v
  else
    docker compose -f "$(basename "$compose_file")" down -v >/dev/null 2>&1
  fi

  local exit_code=$?
  if [ $exit_code -ne 0 ]; then
    log_warn "Failed to teardown Docker Compose for $feature"
  fi

  return 0
}

# Run a complete suite (setup, test, teardown)
run_complete_suite() {
  local suite_info="$1"
  IFS='|' read -r feature_path e2e_dir compose_file <<< "$suite_info"
  local feature=$(get_feature_from_suite "$feature_path")

  local start_time=$(date +%s)

  # Start backend services if enabled
  if [ "$AUTO_START_SERVICES" = "true" ]; then
    if ! start_backend_services "$feature"; then
      log_error "Backend service startup failed for $feature"
      return 1
    fi
  fi

  # Setup Docker Compose (frontends)
  if ! setup_suite "$feature_path" "$e2e_dir" "$compose_file"; then
    log_error "Setup failed for $feature_path"
    return 1
  fi

  # Run tests
  local test_exit_code=0
  if ! run_suite "$feature_path" "$e2e_dir"; then
    test_exit_code=1
  fi

  # Teardown Docker Compose
  teardown_suite "$feature_path" "$e2e_dir" "$compose_file"

  local end_time=$(date +%s)
  local duration=$((end_time - start_time))

  log_suite "$feature_path" "Completed in ${duration}s"

  return $test_exit_code
}

# Main execution
main() {
  log "Parallel E2E Test Orchestrator"
  log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  log "Mode:        $PARALLEL_MODE"
  log "Workers:     $WORKERS per suite"
  log "Build:       $BUILD"
  log "Teardown:    $TEARDOWN"
  log "Verbose:     $VERBOSE"

  if [ ${#FEATURE_PATTERNS[@]} -gt 0 ]; then
    log "Patterns:    ${FEATURE_PATTERNS[*]}"
  else
    log "Patterns:    (all)"
  fi

  if [ ${#PLAYWRIGHT_ARGS[@]} -gt 0 ]; then
    log "Playwright:  ${PLAYWRIGHT_ARGS[*]}"
  fi

  log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

  # Discover suites
  log "Discovering E2E test suites..."
  mapfile -t SUITES < <(discover_suites)

  if [ ${#SUITES[@]} -eq 0 ]; then
    log_error "No E2E test suites found"
    exit 1
  fi

  log "Found ${#SUITES[@]} suite(s):"
  for suite in "${SUITES[@]}"; do
    IFS='|' read -r feature e2e_dir compose_file <<< "$suite"
    log "  - $feature"
    verbose "    E2E dir: $e2e_dir"
    verbose "    Compose: $compose_file"
  done

  echo

  if [ "$DRY_RUN" = "true" ]; then
    log "Dry run - exiting without running tests"
    exit 0
  fi

  # Run suites
  local overall_start=$(date +%s)
  local failed_suites=()

  if [ "$PARALLEL_MODE" = "parallel" ]; then
    log "Running suites in parallel..."

    # Create temp directory for results
    local results_dir=$(mktemp -d)
    trap "rm -rf $results_dir" EXIT

    # Launch all suites in parallel
    local -a pids=()
    for suite in "${SUITES[@]}"; do
      IFS='|' read -r feature _ _ <<< "$suite"
      local result_file="$results_dir/$feature.result"

      # Create parent directory for result file (handles nested features like marketplace/frontend-public)
      mkdir -p "$(dirname "$result_file")"

      (
        if run_complete_suite "$suite"; then
          echo "0" > "$result_file"
        else
          echo "1" > "$result_file"
        fi
      ) &

      pids+=($!)
    done

    # Wait for all to complete
    log "Waiting for ${#pids[@]} suite(s) to complete..."
    for pid in "${pids[@]}"; do
      wait "$pid" || true
    done

    # Collect results
    for suite in "${SUITES[@]}"; do
      IFS='|' read -r feature _ _ <<< "$suite"
      local result_file="$results_dir/$feature.result"

      if [ -f "$result_file" ] && [ "$(cat "$result_file")" != "0" ]; then
        failed_suites+=("$feature")
      fi
    done

  else
    log "Running suites sequentially..."

    for suite in "${SUITES[@]}"; do
      IFS='|' read -r feature _ _ <<< "$suite"

      if ! run_complete_suite "$suite"; then
        failed_suites+=("$feature")
      fi

      echo
    done
  fi

  # Summary
  local overall_end=$(date +%s)
  local overall_duration=$((overall_end - overall_start))

  echo
  log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  log "Summary"
  log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  log "Total suites:  ${#SUITES[@]}"
  log "Passed:        $((${#SUITES[@]} - ${#failed_suites[@]}))"
  log "Failed:        ${#failed_suites[@]}"
  log "Duration:      ${overall_duration}s"

  if [ ${#failed_suites[@]} -gt 0 ]; then
    log_error "Failed suites:"
    for feature in "${failed_suites[@]}"; do
      log_error "  - $feature"
    done

    # Cleanup backend services if we started any
    if [ "$AUTO_START_SERVICES" = "true" ] && [ ${#STARTED_SERVICES[@]} -gt 0 ]; then
      teardown_backend_services
    fi

    exit 1
  fi

  log_success "All E2E tests passed! 🎉"

  # Cleanup backend services if we started any
  if [ "$AUTO_START_SERVICES" = "true" ] && [ ${#STARTED_SERVICES[@]} -gt 0 ]; then
    teardown_backend_services
  fi

  exit 0
}

main
