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

# E2E test script for the AMC spawn workflow.
# Tests the full flow from API call to Zellij pane creation.
#
# Usage:
#   ./tests/e2e_spawn.sh           # Safe mode (no actual spawning)
#   ./tests/e2e_spawn.sh --spawn   # Full test including real agent spawn

SERVER_URL="http://localhost:7400"
TEST_PROJECT="amc"  # Must exist in ~/projects/
AUTH_TOKEN=""
SPAWN_MODE=false
PASSED=0
FAILED=0
SKIPPED=0

# Parse args
for arg in "$@"; do
    case "$arg" in
        --spawn) SPAWN_MODE=true ;;
        *) echo "Unknown arg: $arg"; exit 2 ;;
    esac
done

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log_info()  { echo -e "${GREEN}[INFO]${NC}  $1"; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC}  $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_test()  { echo -e "\n${GREEN}[TEST]${NC}  $1"; }
log_pass()  { echo -e "${GREEN}[PASS]${NC}  $1"; ((PASSED++)); }
log_fail()  { echo -e "${RED}[FAIL]${NC}  $1"; ((FAILED++)); }
log_skip()  { echo -e "${YELLOW}[SKIP]${NC}  $1"; ((SKIPPED++)); }

# Track spawned panes for cleanup
SPAWNED_PANE_NAMES=()

cleanup() {
    if [[ ${#SPAWNED_PANE_NAMES[@]} -gt 0 ]]; then
        log_info "Cleaning up spawned panes..."
        for pane_name in "${SPAWNED_PANE_NAMES[@]}"; do
            # Best-effort: close panes we spawned during tests
            zellij --session infra action close-pane --name "$pane_name" 2>/dev/null || true
        done
    fi
}
trap cleanup EXIT

# ---------------------------------------------------------------------------
# Pre-flight checks
# ---------------------------------------------------------------------------

preflight() {
    log_test "Pre-flight checks"

    # curl available?
    if ! command -v curl &>/dev/null; then
        log_error "curl not found"
        exit 1
    fi

    # jq available?
    if ! command -v jq &>/dev/null; then
        log_error "jq not found (required for JSON assertions)"
        exit 1
    fi

    # Server running?
    if ! curl -sf "${SERVER_URL}/api/health" >/dev/null 2>&1; then
        log_error "Server not running at ${SERVER_URL}"
        log_error "Start with: python -m amc_server.server"
        exit 1
    fi

    # Test project exists?
    if [[ ! -d "$HOME/projects/${TEST_PROJECT}" ]]; then
        log_error "Test project '${TEST_PROJECT}' not found at ~/projects/${TEST_PROJECT}"
        exit 1
    fi

    log_pass "Pre-flight checks passed"
}

# ---------------------------------------------------------------------------
# Extract auth token from dashboard HTML
# ---------------------------------------------------------------------------

extract_auth_token() {
    log_test "Extract auth token from dashboard"

    local html
    html=$(curl -sf "${SERVER_URL}/")

    AUTH_TOKEN=$(echo "$html" | grep -o 'AMC_AUTH_TOKEN = "[^"]*"' | cut -d'"' -f2)
    if [[ -z "$AUTH_TOKEN" ]]; then
        log_error "Could not extract auth token from dashboard HTML"
        log_error "Check that index.html contains <!-- AMC_AUTH_TOKEN --> placeholder"
        exit 1
    fi

    log_pass "Auth token extracted (${AUTH_TOKEN:0:8}...)"
}

# ---------------------------------------------------------------------------
# Test: GET /api/health
# ---------------------------------------------------------------------------

test_health_endpoint() {
    log_test "GET /api/health"

    local response
    response=$(curl -sf "${SERVER_URL}/api/health")

    local ok
    ok=$(echo "$response" | jq -r '.ok')
    if [[ "$ok" != "true" ]]; then
        log_fail "Health endpoint returned ok=$ok"
        return
    fi

    # Must include zellij_available and zellij_session fields
    local has_zellij_available has_zellij_session
    has_zellij_available=$(echo "$response" | jq 'has("zellij_available")')
    has_zellij_session=$(echo "$response" | jq 'has("zellij_session")')

    if [[ "$has_zellij_available" != "true" || "$has_zellij_session" != "true" ]]; then
        log_fail "Health response missing expected fields: $response"
        return
    fi

    local zellij_available
    zellij_available=$(echo "$response" | jq -r '.zellij_available')
    log_pass "Health OK (zellij_available=$zellij_available)"
}

# ---------------------------------------------------------------------------
# Test: GET /api/projects
# ---------------------------------------------------------------------------

test_projects_endpoint() {
    log_test "GET /api/projects"

    local response
    response=$(curl -sf "${SERVER_URL}/api/projects")

    local ok
    ok=$(echo "$response" | jq -r '.ok')
    if [[ "$ok" != "true" ]]; then
        log_fail "Projects endpoint returned ok=$ok"
        return
    fi

    local project_count
    project_count=$(echo "$response" | jq '.projects | length')
    if [[ "$project_count" -lt 1 ]]; then
        log_fail "No projects returned (expected at least 1)"
        return
    fi

    # Verify test project is in the list
    local has_test_project
    has_test_project=$(echo "$response" | jq --arg p "$TEST_PROJECT" '[.projects[] | select(. == $p)] | length')
    if [[ "$has_test_project" -lt 1 ]]; then
        log_fail "Test project '$TEST_PROJECT' not in projects list"
        return
    fi

    log_pass "Projects OK ($project_count projects, '$TEST_PROJECT' present)"
}

# ---------------------------------------------------------------------------
# Test: POST /api/projects/refresh
# ---------------------------------------------------------------------------

test_projects_refresh() {
    log_test "POST /api/projects/refresh"

    local response
    response=$(curl -sf -X POST "${SERVER_URL}/api/projects/refresh")

    local ok
    ok=$(echo "$response" | jq -r '.ok')
    if [[ "$ok" != "true" ]]; then
        log_fail "Projects refresh returned ok=$ok"
        return
    fi

    local project_count
    project_count=$(echo "$response" | jq '.projects | length')
    log_pass "Projects refresh OK ($project_count projects)"
}

# ---------------------------------------------------------------------------
# Test: Spawn without auth (should return 401)
# ---------------------------------------------------------------------------

test_spawn_no_auth() {
    log_test "POST /api/spawn without auth (expect 401)"

    local http_code body
    body=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -d '{"project":"amc","agent_type":"claude"}')

    if [[ "$body" != "401" ]]; then
        log_fail "Expected HTTP 401, got $body"
        return
    fi

    # Also verify the JSON error code
    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -d '{"project":"amc","agent_type":"claude"}')

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "UNAUTHORIZED" ]]; then
        log_fail "Expected code UNAUTHORIZED, got $code"
        return
    fi

    log_pass "Correctly rejected unauthorized request (401/UNAUTHORIZED)"
}

# ---------------------------------------------------------------------------
# Test: Spawn with wrong token (should return 401)
# ---------------------------------------------------------------------------

test_spawn_wrong_token() {
    log_test "POST /api/spawn with wrong token (expect 401)"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer totally-wrong-token" \
        -d '{"project":"amc","agent_type":"claude"}')

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "UNAUTHORIZED" ]]; then
        log_fail "Expected UNAUTHORIZED, got $code"
        return
    fi

    log_pass "Correctly rejected wrong token (UNAUTHORIZED)"
}

# ---------------------------------------------------------------------------
# Test: Spawn with malformed auth (no Bearer prefix)
# ---------------------------------------------------------------------------

test_spawn_malformed_auth() {
    log_test "POST /api/spawn with malformed auth header"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Token ${AUTH_TOKEN}" \
        -d '{"project":"amc","agent_type":"claude"}')

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "UNAUTHORIZED" ]]; then
        log_fail "Expected UNAUTHORIZED for malformed auth, got $code"
        return
    fi

    log_pass "Correctly rejected malformed auth header"
}

# ---------------------------------------------------------------------------
# Test: Spawn with invalid JSON body
# ---------------------------------------------------------------------------

test_spawn_invalid_json() {
    log_test "POST /api/spawn with invalid JSON"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d 'not valid json!!!')

    local ok
    ok=$(echo "$response" | jq -r '.ok')
    if [[ "$ok" != "false" ]]; then
        log_fail "Expected ok=false for invalid JSON, got ok=$ok"
        return
    fi

    log_pass "Correctly rejected invalid JSON body"
}

# ---------------------------------------------------------------------------
# Test: Spawn with path traversal (should return 400/INVALID_PROJECT)
# ---------------------------------------------------------------------------

test_spawn_path_traversal() {
    log_test "POST /api/spawn with path traversal"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d '{"project":"../etc/passwd","agent_type":"claude"}')

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "INVALID_PROJECT" ]]; then
        log_fail "Expected INVALID_PROJECT for path traversal, got $code"
        return
    fi

    log_pass "Correctly rejected path traversal (INVALID_PROJECT)"
}

# ---------------------------------------------------------------------------
# Test: Spawn with nonexistent project
# ---------------------------------------------------------------------------

test_spawn_nonexistent_project() {
    log_test "POST /api/spawn with nonexistent project"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d '{"project":"this-project-does-not-exist-xyz","agent_type":"claude"}')

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "PROJECT_NOT_FOUND" ]]; then
        log_fail "Expected PROJECT_NOT_FOUND, got $code"
        return
    fi

    log_pass "Correctly rejected nonexistent project (PROJECT_NOT_FOUND)"
}

# ---------------------------------------------------------------------------
# Test: Spawn with invalid agent type
# ---------------------------------------------------------------------------

test_spawn_invalid_agent_type() {
    log_test "POST /api/spawn with invalid agent type"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d "{\"project\":\"${TEST_PROJECT}\",\"agent_type\":\"gpt5\"}")

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "INVALID_AGENT_TYPE" ]]; then
        log_fail "Expected INVALID_AGENT_TYPE, got $code"
        return
    fi

    log_pass "Correctly rejected invalid agent type (INVALID_AGENT_TYPE)"
}

# ---------------------------------------------------------------------------
# Test: Spawn with missing project field
# ---------------------------------------------------------------------------

test_spawn_missing_project() {
    log_test "POST /api/spawn with missing project"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d '{"agent_type":"claude"}')

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "MISSING_PROJECT" ]]; then
        log_fail "Expected MISSING_PROJECT, got $code"
        return
    fi

    log_pass "Correctly rejected missing project field (MISSING_PROJECT)"
}

# ---------------------------------------------------------------------------
# Test: Spawn with backslash in project name
# ---------------------------------------------------------------------------

test_spawn_backslash_project() {
    log_test "POST /api/spawn with backslash in project name"

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d '{"project":"foo\\bar","agent_type":"claude"}')

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" != "INVALID_PROJECT" ]]; then
        log_fail "Expected INVALID_PROJECT for backslash, got $code"
        return
    fi

    log_pass "Correctly rejected backslash in project name (INVALID_PROJECT)"
}

# ---------------------------------------------------------------------------
# Test: CORS preflight for /api/spawn
# ---------------------------------------------------------------------------

test_cors_preflight() {
    log_test "OPTIONS /api/spawn (CORS preflight)"

    local http_code headers
    headers=$(curl -sI -X OPTIONS "${SERVER_URL}/api/spawn" 2>/dev/null)
    http_code=$(echo "$headers" | head -1 | grep -o '[0-9][0-9][0-9]' | head -1)

    if [[ "$http_code" != "204" ]]; then
        log_fail "Expected HTTP 204 for OPTIONS, got $http_code"
        return
    fi

    if ! echo "$headers" | grep -qi 'Access-Control-Allow-Methods'; then
        log_fail "Missing Access-Control-Allow-Methods header"
        return
    fi

    if ! echo "$headers" | grep -qi 'Authorization'; then
        log_fail "Authorization not in Access-Control-Allow-Headers"
        return
    fi

    log_pass "CORS preflight OK (204 with correct headers)"
}

# ---------------------------------------------------------------------------
# Test: Actual spawn (only with --spawn flag)
# ---------------------------------------------------------------------------

test_spawn_valid() {
    if [[ "$SPAWN_MODE" != "true" ]]; then
        log_skip "Actual spawn test (pass --spawn to enable)"
        return
    fi

    log_test "POST /api/spawn with valid project (LIVE)"

    # Check Zellij session first
    if ! zellij list-sessions 2>/dev/null | grep -q '^infra'; then
        log_skip "Zellij session 'infra' not found - cannot test live spawn"
        return
    fi

    # Count session files before
    local sessions_dir="$HOME/.local/share/amc/sessions"
    local count_before=0
    if [[ -d "$sessions_dir" ]]; then
        count_before=$(find "$sessions_dir" -name '*.json' -maxdepth 1 2>/dev/null | wc -l | tr -d ' ')
    fi

    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d "{\"project\":\"${TEST_PROJECT}\",\"agent_type\":\"claude\"}")

    local ok
    ok=$(echo "$response" | jq -r '.ok')
    if [[ "$ok" != "true" ]]; then
        local error_code
        error_code=$(echo "$response" | jq -r '.code // .error')
        log_fail "Spawn failed: $error_code"
        return
    fi

    # Verify spawn_id is returned
    local spawn_id
    spawn_id=$(echo "$response" | jq -r '.spawn_id')
    if [[ -z "$spawn_id" || "$spawn_id" == "null" ]]; then
        log_fail "No spawn_id in response"
        return
    fi

    # Track for cleanup
    SPAWNED_PANE_NAMES+=("claude-${TEST_PROJECT}")

    # Verify session_file_found field
    local session_found
    session_found=$(echo "$response" | jq -r '.session_file_found')
    log_info "session_file_found=$session_found, spawn_id=${spawn_id:0:8}..."

    log_pass "Spawn successful (spawn_id=${spawn_id:0:8}...)"
}

# ---------------------------------------------------------------------------
# Test: Rate limiting (only with --spawn flag)
# ---------------------------------------------------------------------------

test_rate_limiting() {
    if [[ "$SPAWN_MODE" != "true" ]]; then
        log_skip "Rate limiting test (pass --spawn to enable)"
        return
    fi

    log_test "Rate limiting on rapid spawn"

    # Immediately try to spawn the same project again
    local response
    response=$(curl -s -X POST "${SERVER_URL}/api/spawn" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -d "{\"project\":\"${TEST_PROJECT}\",\"agent_type\":\"claude\"}")

    local code
    code=$(echo "$response" | jq -r '.code')
    if [[ "$code" == "RATE_LIMITED" ]]; then
        log_pass "Rate limiting active (RATE_LIMITED returned)"
    else
        local ok
        ok=$(echo "$response" | jq -r '.ok')
        log_warn "Rate limiting not triggered (ok=$ok, code=$code) - cooldown may have expired"
        log_pass "Rate limiting test completed (non-deterministic)"
    fi
}

# ---------------------------------------------------------------------------
# Test: Dashboard shows agent after spawn (only with --spawn flag)
# ---------------------------------------------------------------------------

test_dashboard_shows_agent() {
    if [[ "$SPAWN_MODE" != "true" ]]; then
        log_skip "Dashboard agent visibility test (pass --spawn to enable)"
        return
    fi

    log_test "Dashboard /api/state includes spawned agent"

    # Give the session a moment to register
    sleep 2

    local response
    response=$(curl -sf "${SERVER_URL}/api/state")

    local session_count
    session_count=$(echo "$response" | jq '.sessions | length')

    if [[ "$session_count" -gt 0 ]]; then
        log_pass "Dashboard shows $session_count session(s)"
    else
        log_warn "No sessions visible yet (agent may still be starting)"
        log_pass "Dashboard state endpoint responsive"
    fi
}

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

main() {
    echo "========================================="
    echo "  AMC Spawn Workflow E2E Tests"
    echo "========================================="
    echo ""
    if [[ "$SPAWN_MODE" == "true" ]]; then
        log_warn "SPAWN MODE: will create real Zellij panes"
    else
        log_info "Safe mode (no actual spawning). Pass --spawn to test live spawn."
    fi

    preflight
    extract_auth_token

    # Read-only endpoint tests
    test_health_endpoint
    test_projects_endpoint
    test_projects_refresh

    # Auth / validation tests (no side effects)
    test_spawn_no_auth
    test_spawn_wrong_token
    test_spawn_malformed_auth
    test_spawn_invalid_json
    test_spawn_path_traversal
    test_spawn_nonexistent_project
    test_spawn_invalid_agent_type
    test_spawn_missing_project
    test_spawn_backslash_project

    # CORS
    test_cors_preflight

    # Live spawn tests (only with --spawn)
    test_spawn_valid
    test_rate_limiting
    test_dashboard_shows_agent

    # Summary
    echo ""
    echo "========================================="
    echo "  Results: ${PASSED} passed, ${FAILED} failed, ${SKIPPED} skipped"
    echo "========================================="

    if [[ "$FAILED" -gt 0 ]]; then
        exit 1
    fi
}

main "$@"
