#!/bin/bash

# E2E Test Runner Script
# This script starts backend, frontend, and runs E2E tests in Docker automatically.
#
# The script auto-detects pnpm and Node.js 24+ regardless of how it's invoked
# (interactive shell, non-interactive subprocess, CI, Claude Code, etc.)

set -e  # Exit on error

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
MONOREPO_ROOT=<% monorepoRootExpr %>

# Ports
BACKEND_PORT=3001
FRONTEND_PORT=3000
BACKEND_MAIN_PORT=4000

# PID file paths
BACKEND_PID_FILE="$PROJECT_DIR/.e2e-backend.pid"
FRONTEND_PID_FILE="$PROJECT_DIR/.e2e-frontend.pid"

# Docker image name
DOCKER_IMAGE="e2e-pxl-env"

# --------------------------------------------------------------------------
# Environment setup: resolve pnpm and Node.js 24+
# --------------------------------------------------------------------------
# This section ensures pnpm and the correct Node.js version are available
# even in non-interactive shells (e.g., Claude Code, CI, cron, subprocesses).

setup_environment() {
    # Common paths where pnpm might be installed
    local search_paths=(
        "$HOME/.local/share/pnpm"
        "$HOME/.pnpm"
        "/opt/homebrew/bin"
        "/usr/local/bin"
        "$HOME/.corepack/bin"
    )

    # Add search paths to PATH if they exist and aren't already in PATH
    for p in "${search_paths[@]}"; do
        if [[ -d "$p" ]] && [[ ":$PATH:" != *":$p:"* ]]; then
            export PATH="$p:$PATH"
        fi
    done

    # Resolve pnpm
    PNPM_BIN=$(command -v pnpm 2>/dev/null || true)
    if [[ -z "$PNPM_BIN" ]]; then
        echo -e "${RED}❌ pnpm not found. Please install pnpm (https://pnpm.io/installation)${NC}"
        exit 1
    fi

    # Check current Node.js version - we need 24+ for ESM require() support
    local node_version
    node_version=$(node --version 2>/dev/null | sed 's/^v//' | cut -d. -f1)

    if [[ -n "$node_version" ]] && [[ "$node_version" -ge 24 ]]; then
        echo -e "${GREEN}✅ Node.js $(node --version) detected${NC}"
        return 0
    fi

    # Try to activate Node.js 24+ via nvm
    if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
        export NVM_DIR="$HOME/.nvm"
        # shellcheck source=/dev/null
        source "$NVM_DIR/nvm.sh" 2>/dev/null

        # Find a Node.js 24+ version installed via nvm
        local nvm_node
        nvm_node=$(nvm ls --no-alias --no-colors 2>/dev/null | grep -oE 'v2[4-9]\.[0-9]+\.[0-9]+|v[3-9][0-9]\.[0-9]+\.[0-9]+' | sort -V | tail -1 || true)

        if [[ -n "$nvm_node" ]]; then
            echo -e "${BLUE}🔄 Activating $nvm_node via nvm...${NC}"
            nvm use "$nvm_node" >/dev/null 2>&1
            # Ensure nvm's bin dir is first in PATH so pnpm picks it up
            export PATH="$NVM_BIN:$PATH"
            echo -e "${GREEN}✅ Node.js $(node --version) activated via nvm${NC}"
            return 0
        fi
    fi

    # Try to activate Node.js 24+ via fnm
    if command -v fnm >/dev/null 2>&1; then
        local fnm_node
        fnm_node=$(fnm list 2>/dev/null | grep -oE 'v2[4-9]\.[0-9]+\.[0-9]+|v[3-9][0-9]\.[0-9]+\.[0-9]+' | sort -V | tail -1 || true)

        if [[ -n "$fnm_node" ]]; then
            echo -e "${BLUE}🔄 Activating $fnm_node via fnm...${NC}"
            eval "$(fnm env)" 2>/dev/null
            fnm use "$fnm_node" >/dev/null 2>&1
            echo -e "${GREEN}✅ Node.js $(node --version) activated via fnm${NC}"
            return 0
        fi
    fi

    # Try to activate Node.js 24+ via volta
    if command -v volta >/dev/null 2>&1; then
        echo -e "${BLUE}🔄 Using volta for Node.js management...${NC}"
        # volta manages node automatically based on package.json
        local volta_node
        volta_node=$(node --version 2>/dev/null | sed 's/^v//' | cut -d. -f1)
        if [[ "$volta_node" -ge 24 ]]; then
            echo -e "${GREEN}✅ Node.js $(node --version) via volta${NC}"
            return 0
        fi
    fi

    # Node.js 24+ not found - warn but don't fail (pnpm devEngines might handle it)
    echo -e "${YELLOW}⚠️  Node.js 24+ not found (current: $(node --version 2>/dev/null || echo 'none')).${NC}"
    echo -e "${YELLOW}   The project requires Node.js 24+ for ESM support.${NC}"
    echo -e "${YELLOW}   Install via: nvm install 24 / fnm install 24 / volta install node@24${NC}"
    echo -e "${YELLOW}   Continuing anyway (pnpm devEngines.runtime may auto-download it)...${NC}"
}

# Kill a process and all its descendants (children, grandchildren, etc.)
kill_tree() {
    local pid=$1
    local children
    children=$(pgrep -P "$pid" 2>/dev/null || true)
    for child in $children; do
        kill_tree "$child"
    done
    kill "$pid" 2>/dev/null || true
}

# Kill any remaining process listening on a port
kill_port() {
    local port=$1
    local pids
    pids=$(lsof -Pi :"$port" -sTCP:LISTEN -t 2>/dev/null || true)
    if [[ -n "$pids" ]]; then
        echo -e "${BLUE}   Killing remaining process(es) on port $port...${NC}"
        echo "$pids" | xargs kill 2>/dev/null || true
    fi
}

# Cleanup function
cleanup() {
    echo -e "\n${YELLOW}🧹 Cleaning up...${NC}"

    # Kill backend if we started it
    if [[ -f "$BACKEND_PID_FILE" ]]; then
        BACKEND_PID=$(cat "$BACKEND_PID_FILE")
        if ps -p "$BACKEND_PID" > /dev/null 2>&1; then
            echo -e "${BLUE}⏹️  Stopping backend (PID: $BACKEND_PID)...${NC}"
            kill_tree "$BACKEND_PID"
        fi
        rm -f "$BACKEND_PID_FILE"
        # Ensure ports are fully released
        sleep 0.5
        kill_port $BACKEND_PORT
        kill_port $BACKEND_MAIN_PORT
    fi

    # Kill frontend if we started it
    if [[ -f "$FRONTEND_PID_FILE" ]]; then
        FRONTEND_PID=$(cat "$FRONTEND_PID_FILE")
        if ps -p "$FRONTEND_PID" > /dev/null 2>&1; then
            echo -e "${BLUE}⏹️  Stopping frontend (PID: $FRONTEND_PID)...${NC}"
            kill_tree "$FRONTEND_PID"
        fi
        rm -f "$FRONTEND_PID_FILE"
        # Ensure port is fully released
        sleep 0.5
        kill_port $FRONTEND_PORT
    fi

    echo -e "${GREEN}✅ Cleanup complete${NC}"
}

# Register cleanup on script exit
trap cleanup EXIT INT TERM

# Check if services are already running
check_services() {
    echo -e "${BLUE}🔍 Checking for existing services...${NC}"

    BACKEND_RUNNING=false
    FRONTEND_RUNNING=false

    if lsof -Pi :$BACKEND_PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
        echo -e "${YELLOW}⚠️  Backend already running on port $BACKEND_PORT${NC}"
        BACKEND_RUNNING=true
    fi

    if lsof -Pi :$FRONTEND_PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
        echo -e "${YELLOW}⚠️  Frontend already running on port $FRONTEND_PORT${NC}"
        FRONTEND_RUNNING=true
    fi
}

# Wait for a service to be ready
wait_for_service() {
    local port=$1
    local service_name=$2
    local max_attempts=60  # 60 seconds timeout
    local attempt=0

    echo -e "${BLUE}⏳ Waiting for $service_name to be ready on port $port...${NC}"

    while ! lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; do
        attempt=$((attempt + 1))
        if [[ $attempt -gt $max_attempts ]]; then
            echo -e "${RED}❌ Timeout waiting for $service_name to start${NC}"
            return 1
        fi
        sleep 1
    done

    echo -e "${GREEN}✅ $service_name is ready${NC}"
    return 0
}

# Start backend
start_backend() {
    if [[ "$BACKEND_RUNNING" == "true" ]]; then
        echo -e "${GREEN}✅ Using existing backend on port $BACKEND_PORT${NC}"
        return 0
    fi

    echo -e "${BLUE}🚀 Starting backend in E2E stateless mode...${NC}"
    cd "$PROJECT_DIR/backend"

    # Start backend in background and save PID
    "$PNPM_BIN" e2e:stateless > "$PROJECT_DIR/.e2e-backend.log" 2>&1 &
    echo $! > "$BACKEND_PID_FILE"

    # Wait for backend to be ready
    if ! wait_for_service $BACKEND_PORT "Backend"; then
        echo -e "${RED}❌ Failed to start backend${NC}"
        echo -e "${YELLOW}📋 Backend log (last 20 lines):${NC}"
        tail -n 20 "$PROJECT_DIR/.e2e-backend.log"
        return 1
    fi
}

# Build and start frontend
start_frontend() {
    if [[ "$FRONTEND_RUNNING" == "true" ]]; then
        echo -e "${GREEN}✅ Using existing frontend on port $FRONTEND_PORT${NC}"
        return 0
    fi

    echo -e "${BLUE}🏗️  Building frontend for E2E...${NC}"
    cd "$PROJECT_DIR/frontend"

    # Build frontend with Docker URLs (tests run inside Docker container accessing host services)
    if ! VITE_PUBLIC_API_URL=http://host.docker.internal:3001 VITE_PUBLIC_BASE_URL=http://host.docker.internal:3000 NODE_V8_COVERAGE=.v8-coverage VITE_PUBLIC_USE_TEST_ASSETS=true VITE_AUTH=false "$PNPM_BIN" exec vite build; then
        echo -e "${RED}❌ Failed to build frontend${NC}"
        return 1
    fi

    echo -e "${BLUE}🚀 Starting frontend preview server...${NC}"

    # Start frontend preview server in background and save PID
    "$PNPM_BIN" exec vite preview --port 3000 > "$PROJECT_DIR/.e2e-frontend.log" 2>&1 &
    echo $! > "$FRONTEND_PID_FILE"

    # Wait for frontend to be ready
    if ! wait_for_service $FRONTEND_PORT "Frontend"; then
        echo -e "${RED}❌ Failed to start frontend${NC}"
        echo -e "${YELLOW}📋 Frontend log (last 20 lines):${NC}"
        tail -n 20 "$PROJECT_DIR/.e2e-frontend.log"
        return 1
    fi
}

# Build Docker image
build_docker_image() {
    echo -e "${BLUE}🐳 Building Docker image...${NC}"
    cd "$PROJECT_DIR"

    if docker build -t $DOCKER_IMAGE ./e2e/; then
        echo -e "${GREEN}✅ Docker image built successfully${NC}"
        return 0
    else
        echo -e "${RED}❌ Failed to build Docker image${NC}"
        return 1
    fi
}

# Run E2E tests in Docker
run_tests() {
    echo -e "${BLUE}🧪 Running E2E tests in Docker...${NC}"

    # Build Docker image if it doesn't exist
    if ! docker image inspect $DOCKER_IMAGE >/dev/null 2>&1; then
        echo -e "${YELLOW}Docker image not found, building...${NC}"
        if ! build_docker_image; then
            return 1
        fi
    fi

    echo -e "${BLUE}📦 Running tests in Docker container...${NC}"

    # Clean test output directories so they can be recreated with correct permissions
    rm -rf "$MONOREPO_ROOT<% e2eArtifactsPath %>/test-results" "$MONOREPO_ROOT<% e2eArtifactsPath %>/playwright-report" "$MONOREPO_ROOT<% e2eArtifactsPath %>/monocart-report"

    # Run tests in Docker with proper volume mounting
    # Use host user ID to avoid permission issues with mounted volumes
    docker run --rm \
        --user "$(id -u):$(id -g)" \
        -v "$MONOREPO_ROOT:/pxl" \
        -w <% dockerWorkDir %> \
        --add-host=host.docker.internal:host-gateway \
        $DOCKER_IMAGE \
        /bin/bash -c "<% nodeShimsCleanup %>; pnpm e2e"

    local test_exit_code=$?

    if [[ $test_exit_code -eq 0 ]]; then
        echo -e "${GREEN}✅ All tests passed!${NC}"
    else
        echo -e "${RED}❌ Tests failed with exit code $test_exit_code${NC}"
    fi

    return $test_exit_code
}

# Main execution
main() {
    echo -e "${GREEN}╔═══════════════════╗${NC}"
    echo -e "${GREEN}║  E2E Test Runner  ║${NC}"
    echo -e "${GREEN}╚═══════════════════╝${NC}\n"

    echo -e "${BLUE}🐳 Running in Docker mode${NC}\n"

    # Setup environment (resolve pnpm, activate Node.js 24+)
    setup_environment

    # Check for existing services
    check_services

    # Start backend
    if ! start_backend; then
        echo -e "${RED}❌ Failed to start backend. Exiting.${NC}"
        exit 1
    fi

    # Start frontend
    if ! start_frontend; then
        echo -e "${RED}❌ Failed to start frontend. Exiting.${NC}"
        exit 1
    fi

    # Run tests in Docker
    run_tests
    local exit_code=$?

    exit $exit_code
}

# Parse command line arguments
case "${1:-}" in
    --help|-h)
        echo "Usage: $0 [OPTIONS]"
        echo ""
        echo "Options:"
        echo "  --help, -h      Show this help message"
        echo "  --build-image   Build Docker image and exit"
        echo ""
        echo "This script automatically:"
        echo "  1. Detects pnpm and activates Node.js 24+ (via nvm/fnm/volta)"
        echo "  2. Checks if backend/frontend are already running (reuses them)"
        echo "  3. Starts backend in E2E stateless mode (if needed)"
        echo "  4. Builds and starts frontend preview server (if needed)"
        echo "  5. Runs E2E tests in Docker"
        echo "  6. Cleans up all processes on exit (Ctrl+C safe)"
        echo ""
        echo "Examples:"
        echo "  $0                    # Run E2E tests"
        echo "  $0 --build-image      # Just build the Docker image"
        echo ""
        echo "Logs:"
        echo "  Backend log:  .e2e-backend.log"
        echo "  Frontend log: .e2e-frontend.log"
        exit 0
        ;;
    --build-image)
        setup_environment
        build_docker_image
        exit $?
        ;;
    *)
        main
        ;;
esac
