#!/usr/bin/env bash
# #region run-enterprise-clean-e2e [C:5] [TYPE Script] [SEMANTICS e2e, enterprise-clean, orchestration, bundle, test]
# @BRIEF Enterprise Clean E2E Orchestrator — полный протокол тестирования пустого образа перед передачей.
#   Stage 1: Сборка dist bundle, развёртывание enterprise-clean стека, PostgreSQL, Playwright проверка
#            первоначальной настройки (StartupEnvironmentWizard), добавление Superset окружения.
#   Stage 2: Запуск ./run.sh (локальный dev стек), Playwright проверка живого проекта,
#            LLM анализ дашборда, изменение настроек.
# @PRE Docker и docker compose установлены. Node 20+, python3, .venv настроен.
# @POST Stage 1: Enterprise-clean bundle собран, развёрнут, протестирован, стек остановлен.
#       Stage 2: Локальный dev стек запущен, протестирован, остановлен.
# @SIDE_EFFECT Создаёт bundle в dist/docker/. Запускает и останавливает Docker контейнеры.
#   Останавливает dev процессы (uvicorn, vite). Использует временный PostgreSQL контейнер.
# @INVARIANT Stage 1 и Stage 2 выполняются последовательно. Ошибка в Stage 1 прерывает весь протокол.
# @INVARIANT После завершения (успех или ошибка) все контейнеры и процессы останавливаются.
# @LAYER Infrastructure
# @RELATION DEPENDS_ON -> [build.sh]
# @RELATION DEPENDS_ON -> [run.sh]
# @RELATION DEPENDS_ON -> [../playwright.config.js]
# @RELATION DEPENDS_ON -> [./tests/enterprise-clean-setup.e2e.js]
# @RELATION DEPENDS_ON -> [./tests/live-project-check.e2e.js]
# @RATIONALE Пустой образ (enterprise-clean bundle) требует полного цикла проверки: от сборки до
#   развёртывания и первичной настройки. Без этой orchestration QA не может гарантировать,
#   что образ ready-to-deliver. Разделение на 2 stage позволяет проверить как "чистую" установку
#   (fresh PostgreSQL, StartupEnvironmentWizard), так и "живой" проект с настроенными окружениями.
# @REJECTED Использование одного docker-compose файла для обоих стейджей — enterprise-clean требует
#   build из исходников + внешний PostgreSQL, а run.sh использует локальный dev compose.
# @DATA_CONTRACT Input: тег релиза (аргумент), .env.enterprise-clean.example, backend/.env
#   Output: dist/docker/*.tar.xz, dist/docker/manifest*.txt, playwright-report/
# #endregion run-enterprise-clean-e2e

set -euo pipefail

# ======================================================================
#  CONFIGURATION
# ======================================================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
FRONTEND_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
PROJECT_ROOT="$(cd "$FRONTEND_DIR/.." && pwd)"

# Default tag (timestamp-based)
RELEASE_TAG="${1:-e2e-test-$(date +%Y%m%d-%H%M%S)}"

# Stage 1: Enterprise Clean ports
EC_BACKEND_PORT=8104
EC_FRONTEND_PORT=8105
EC_POSTGRES_PORT=5436

# Stage 2: Live project (run.sh defaults)
# Stage 2: Live project (run.sh defaults — avoid conflicts with enterprise-clean on 8000/8001)
LIVE_BACKEND_PORT=8106
LIVE_FRONTEND_PORT=5173

# Таймауты
TIMEOUT_BACKEND_START=60
TIMEOUT_FRONTEND_START=30

# Цвета для вывода
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# ======================================================================
#  HELP
# ======================================================================
show_help() {
    cat <<'HELP'
Usage: ./e2e/run-enterprise-clean-e2e.sh [release-tag]

Enterprise Clean E2E Test Orchestrator — полный протокол тестирования пустого образа.

Протокол:
  Stage 1 — Empty Image Setup:
    1. Сборка enterprise-clean bundle (./build.sh bundle <tag>)
    2. Загрузка образов в Docker (xz -dc ... | docker load)
    3. Запуск PostgreSQL контейнера
    4. Развёртывание enterprise-clean стека
    5. Playwright: логин, StartupEnvironmentWizard, добавление ss-dev.bebesh.ru
    6. Остановка enterprise-clean стека

  Stage 2 — Live Project Verification:
    1. Запуск ./run.sh (backend + frontend)
    2. Playwright: логин (admin/admin123), LLM проверка дашборда, изменение настроек
    3. Остановка dev процессов

Аргументы:
  release-tag    Тег релиза (по умолчанию: e2e-test-<timestamp>)

Пример:
  ./e2e/run-enterprise-clean-e2e.sh v2.1.0
HELP
    exit 0
}

if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
    show_help
fi

# ======================================================================
#  UTILITY FUNCTIONS
# ======================================================================

log_info()  { echo -e "${BLUE}[E2E]${NC} $*"; }
log_ok()    { echo -e "${GREEN}[E2E] ✅${NC} $*"; }
log_warn()  { echo -e "${YELLOW}[E2E] ⚠️${NC} $*"; }
log_error() { echo -e "${RED}[E2E] ❌${NC} $*"; }

cleanup() {
    local exit_code=$?
    log_info "Cleanup: stopping all services..."

    # Stop Stage 2 processes (run.sh)
    if [[ -n "${BACKEND_PID:-}" ]]; then
        kill "$BACKEND_PID" 2>/dev/null || true
    fi
    if [[ -n "${FRONTEND_PID:-}" ]]; then
        kill "$FRONTEND_PID" 2>/dev/null || true
    fi

    # Stop Stage 1 docker compose
    if [[ -f "${PROJECT_ROOT}/dist/docker/docker-compose.enterprise-clean.yml" ]]; then
        log_info "Stopping enterprise-clean stack..."
        docker compose -p ss-tools-e2e-ec \
            -f "${PROJECT_ROOT}/dist/docker/docker-compose.enterprise-clean.yml" \
            down -t 10 2>/dev/null || true
    fi

    # Stop PostgreSQL container
    docker rm -f ss-tools-e2e-pg 2>/dev/null || true

    log_info "Cleanup complete (exit code: $exit_code)"
    exit "$exit_code"
}

trap cleanup EXIT INT TERM

wait_for_backend() {
    local url="$1"
    local timeout="${2:-$TIMEOUT_BACKEND_START}"
    log_info "Waiting for backend at $url (timeout: ${timeout}s)..."
    for i in $(seq 1 "$timeout"); do
        local http_code
        http_code=$(curl -s -o /dev/null -w "%{http_code}" "${url}/api/health/summary" 2>/dev/null || echo "000")
        # 200 = healthy, 401/403 = up but needs auth (still means server is running)
        if [[ "$http_code" == "200" || "$http_code" == "401" || "$http_code" == "403" ]]; then
            log_ok "Backend responding (HTTP ${http_code}) after ${i}s"
            return 0
        fi
        sleep 1
    done
    log_error "Backend failed to start within ${timeout}s"
    return 1
}

wait_for_frontend() {
    local url="$1"
    local timeout="${2:-$TIMEOUT_FRONTEND_START}"
    log_info "Waiting for frontend at $url (timeout: ${timeout}s)..."
    for i in $(seq 1 "$timeout"); do
        if curl -sf -o /dev/null "${url}/login" 2>/dev/null; then
            log_ok "Frontend reachable after ${i}s"
            return 0
        fi
        sleep 1
    done
    log_error "Frontend failed to start within ${timeout}s"
    return 1
}

run_playwright_tests() {
    local grep_pattern="$1"
    local frontend_url="$2"
    local backend_url="$3"
    local description="$4"

    log_info "Running Playwright tests: ${description}..."

    cd "$FRONTEND_DIR"

    BACKEND_URL="$backend_url" \
    FRONTEND_URL="$frontend_url" \
    E2E_USERNAME="admin" \
    E2E_PASSWORD="admin123" \
    npx playwright test --grep "$grep_pattern" \
        --reporter=list,html \
        || {
            local exit_code=$?
            log_warn "Playwright tests completed with exit code: $exit_code"
            return "$exit_code"
        }

    log_ok "Playwright tests passed: ${description}"
}

# ======================================================================
#  STAGE 1: Enterprise Clean — Empty Image Setup
# ======================================================================
stage1_enterprise_clean() {
    echo ""
    echo "═══════════════════════════════════════════════════════════════"
    echo "  STAGE 1: Enterprise Clean — Empty Image Setup"
    echo "  Bundle tag: ${RELEASE_TAG}"
    echo "═══════════════════════════════════════════════════════════════"
    echo ""

    # ── 1.1. CREATE DIST BUNDLE ──────────────────────────────
    log_info "Step 1.1: Building enterprise-clean bundle '${RELEASE_TAG}'..."
    cd "$PROJECT_ROOT"

    if ! ./build.sh bundle "${RELEASE_TAG}"; then
        log_error "Bundle build failed"
        return 1
    fi
    log_ok "Bundle built: dist/docker/"

    # ── 1.2. LOAD IMAGES ─────────────────────────────────────
    log_info "Step 1.2: Loading Docker images from bundle..."
    if [[ -f "${PROJECT_ROOT}/dist/docker/backend.${RELEASE_TAG}.tar.xz" ]]; then
        xz -dc "${PROJECT_ROOT}/dist/docker/backend.${RELEASE_TAG}.tar.xz" | docker load
        log_ok "Backend image loaded"
    else
        log_warn "Backend archive not found, attempting docker build instead..."
        cd "$PROJECT_ROOT"
        docker build -f docker/backend.Dockerfile -t "ss-tools-backend:${RELEASE_TAG}" .
    fi

    if [[ -f "${PROJECT_ROOT}/dist/docker/frontend.${RELEASE_TAG}.tar.xz" ]]; then
        xz -dc "${PROJECT_ROOT}/dist/docker/frontend.${RELEASE_TAG}.tar.xz" | docker load
        log_ok "Frontend image loaded"
    else
        log_warn "Frontend archive not found, attempting docker build instead..."
        cd "$PROJECT_ROOT"
        docker build -f docker/frontend.Dockerfile -t "ss-tools-frontend:${RELEASE_TAG}" .
    fi

    # ── 1.3. START POSTGRESQL (on default bridge first) ─────
    log_info "Step 1.3: Starting new PostgreSQL container..."
    docker rm -f ss-tools-e2e-pg 2>/dev/null || true

    # Remove any stale compose network so docker compose creates it fresh
    docker network rm ss-tools-e2e-ec_default 2>/dev/null || true

    # Start PG on default bridge initially — compose will create its own network
    docker run -d \
        --name ss-tools-e2e-pg \
        -e POSTGRES_DB=ss_tools \
        -e POSTGRES_USER=postgres \
        -e POSTGRES_PASSWORD=postgres \
        -p "${EC_POSTGRES_PORT}:5432" \
        postgres:16-alpine

    log_info "Waiting for PostgreSQL to be ready..."
    for i in $(seq 1 20); do
        if docker exec ss-tools-e2e-pg pg_isready -U postgres -d ss_tools > /dev/null 2>&1; then
            log_ok "PostgreSQL ready after ${i}s"
            break
        fi
        if [ "$i" -eq 20 ]; then
            log_error "PostgreSQL failed to start"
            docker logs ss-tools-e2e-pg --tail 20
            return 1
        fi
        sleep 1
    done

    EC_PG_HOST="ss-tools-e2e-pg"
    EC_PG_PORT="5432"

    # ── 1.4. DEPLOY ENTERPRISE CLEAN STACK ───────────────────
    log_info "Step 1.4: Deploying enterprise-clean stack..."
    log_info "  PostgreSQL: ${EC_PG_HOST}:${EC_PG_PORT}"
    log_info "  Backend port: ${EC_BACKEND_PORT}"
    log_info "  Frontend port: ${EC_FRONTEND_PORT}"

    # Generate a minimal env file for E2E testing
    cat > "/tmp/.env.e2e-enterprise-clean" <<EOF
POSTGRES_HOST=${EC_PG_HOST}
POSTGRES_PORT=${EC_PG_PORT}
POSTGRES_DB=ss_tools
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
BACKEND_HOST_PORT=${EC_BACKEND_PORT}
FRONTEND_HOST_PORT=${EC_FRONTEND_PORT}
FRONTEND_SSL_PORT=8443
INITIAL_ADMIN_CREATE=true
INITIAL_ADMIN_USERNAME=admin
INITIAL_ADMIN_PASSWORD=admin123
INITIAL_ADMIN_EMAIL=admin@example.com
ENABLE_BELIEF_STATE_LOGGING=true
TASK_LOG_LEVEL=INFO
FEATURES__DATASET_REVIEW=true
FEATURES__HEALTH_MONITOR=true
CERTS_PATH=./certs
EOF

    # Use the generated compose file from the bundle, or fall back to source compose
    EC_COMPOSE_FILE="${PROJECT_ROOT}/dist/docker/docker-compose.enterprise-clean.yml"
    if [[ ! -f "$EC_COMPOSE_FILE" ]]; then
        EC_COMPOSE_FILE="${PROJECT_ROOT}/docker-compose.enterprise-clean.yml"
        log_info "Using source docker-compose.enterprise-clean.yml (no pre-built images)"
        # Override image tags in compose to use our built images
        export BACKEND_TAG="${RELEASE_TAG}"
        export FRONTEND_TAG="${RELEASE_TAG}"
    fi

    # Start compose FIRST so it creates the network with proper compose labels.
    # The backend will fail to connect to PG on first start (PG not on compose network
    # yet), but Docker restart policy (unless-stopped) will retry.
    docker compose -p ss-tools-e2e-ec \
        -f "$EC_COMPOSE_FILE" \
        --env-file "/tmp/.env.e2e-enterprise-clean" \
        up -d

    # Connect PG to the compose-created network so backend can resolve ss-tools-e2e-pg
    docker network connect ss-tools-e2e-ec_default ss-tools-e2e-pg

    # Restart backend so entrypoint reruns with PG accessible
    docker compose -p ss-tools-e2e-ec restart backend

    # ── 1.5. WAIT FOR BACKEND + FRONTEND ────────────────────
    log_info "Step 1.5: Waiting for services..."

    if ! wait_for_backend "http://127.0.0.1:${EC_BACKEND_PORT}" "$TIMEOUT_BACKEND_START"; then
        log_error "Backend failed to start. Logs:"
        docker compose -p ss-tools-e2e-ec logs --tail=30 backend
        return 1
    fi

    if ! wait_for_frontend "http://127.0.0.1:${EC_FRONTEND_PORT}" "$TIMEOUT_FRONTEND_START"; then
        log_error "Frontend failed to start. Logs:"
        docker compose -p ss-tools-e2e-ec logs --tail=30 frontend
        return 1
    fi

    # ── 1.6. RUN PLAYWRIGHT — STAGE 1 ───────────────────────
    log_info "Step 1.6: Running Stage 1 Playwright tests..."
    export E2E_TOKEN=""

    # Get auth token first
    LOGIN_RESPONSE=$(curl -s -X POST "http://127.0.0.1:${EC_BACKEND_PORT}/api/auth/login" \
        -H "Content-Type: application/x-www-form-urlencoded" \
        -d "username=admin&password=admin123")
    E2E_TOKEN=$(echo "$LOGIN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || echo "")
    export E2E_TOKEN

    if [[ -z "$E2E_TOKEN" ]]; then
        log_warn "Could not obtain auth token (may be normal on first startup with wizard)"
    fi

    cd "$FRONTEND_DIR"

    BACKEND_URL="http://127.0.0.1:${EC_BACKEND_PORT}" \
    FRONTEND_URL="http://127.0.0.1:${EC_FRONTEND_PORT}" \
    E2E_TOKEN="${E2E_TOKEN}" \
    E2E_USERNAME="admin" \
    E2E_PASSWORD="admin123" \
    npx playwright test --grep "Enterprise Clean Setup" \
        --reporter=list,html \
        || {
            local ec_exit=$?
            log_warn "Stage 1 Playwright tests exited with code: $ec_exit"
            # Continue despite failure — we want logs
        }

    log_ok "Stage 1 complete!"

    # ── 1.7. STOP ENTERPRISE CLEAN STACK ─────────────────────
    log_info "Step 1.7: Stopping enterprise-clean stack..."
    docker compose -p ss-tools-e2e-ec \
        -f "$EC_COMPOSE_FILE" \
        --env-file "/tmp/.env.e2e-enterprise-clean" \
        down -t 10

    docker rm -f ss-tools-e2e-pg 2>/dev/null || true
    log_ok "Stage 1: Enterprise clean stack stopped"
}

# ======================================================================
#  STAGE 2: Live Project Verification
# ======================================================================
stage2_live_project() {
    echo ""
    echo "═══════════════════════════════════════════════════════════════"
    echo "  STAGE 2: Live Project Verification"
    echo "  Run: ./run.sh (local dev stack)"
    echo "═══════════════════════════════════════════════════════════════"
    echo ""

    # ── 2.1. ENSURE POSTGRESQL RUNNING ───────────────────────
    log_info "Step 2.1: Ensuring PostgreSQL is running for local dev stack..."
    cd "$PROJECT_ROOT"

    # Check if PostgreSQL is already running on standard port
    if python3 -c "import socket; socket.create_connection(('localhost', 5432), timeout=1).close()" 2>/dev/null; then
        log_ok "PostgreSQL already running on localhost:5432"
    else
        log_info "Starting PostgreSQL via docker compose..."
        docker compose -p ss-tools-current -f docker-compose.yml up -d db
        log_info "Waiting for PostgreSQL..."
        for i in $(seq 1 20); do
            if python3 -c "import socket; socket.create_connection(('localhost', 5432), timeout=1).close()" 2>/dev/null; then
                log_ok "PostgreSQL ready after ${i}s"
                break
            fi
            if [ "$i" -eq 20 ]; then
                log_error "PostgreSQL failed to start"
                docker compose -p ss-tools-current logs db --tail 20
                return 1
            fi
            sleep 1
        done
    fi

    # ── 2.2. START BACKEND ───────────────────────────────────
    log_info "Step 2.2: Starting backend (uvicorn)..."
    cd "$PROJECT_ROOT/backend"

    # Activate virtual environment
    if [[ -f ".venv/bin/activate" ]]; then
        source .venv/bin/activate
    else
        log_warn "No .venv found, trying system python"
    fi

    python3 -m uvicorn src.app:app \
        --reload \
        --port "${LIVE_BACKEND_PORT}" \
        --env-file .env \
        2>&1 | sed "s/^/[Backend] /" &
    BACKEND_PID=$!
    cd "$PROJECT_ROOT"

    log_info "Backend PID: ${BACKEND_PID}"

    if ! wait_for_backend "http://127.0.0.1:${LIVE_BACKEND_PORT}" "$TIMEOUT_BACKEND_START"; then
        log_error "Backend failed to start"
        return 1
    fi

    # ── 2.3. START FRONTEND ──────────────────────────────────
    log_info "Step 2.3: Starting frontend (vite)..."
    cd "$FRONTEND_DIR"

    npm run dev -- --port "${LIVE_FRONTEND_PORT}" \
        2>&1 | sed "s/^/[Frontend] /" &
    FRONTEND_PID=$!
    cd "$PROJECT_ROOT"

    log_info "Frontend PID: ${FRONTEND_PID}"

    if ! wait_for_frontend "http://127.0.0.1:${LIVE_FRONTEND_PORT}" "$TIMEOUT_FRONTEND_START"; then
        log_error "Frontend failed to start"
        return 1
    fi

    # ── 2.4. OBTAIN AUTH TOKEN ───────────────────────────────
    log_info "Step 2.4: Obtaining auth token..."
    LOGIN_RESPONSE=$(curl -s -X POST "http://127.0.0.1:${LIVE_BACKEND_PORT}/api/auth/login" \
        -H "Content-Type: application/x-www-form-urlencoded" \
        -d "username=admin&password=admin123")
    E2E_TOKEN=$(echo "$LOGIN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || echo "")
    export E2E_TOKEN

    if [[ -z "$E2E_TOKEN" ]]; then
        log_error "Failed to obtain auth token"
        log_error "Login response: $LOGIN_RESPONSE"
        return 1
    fi
    log_ok "Auth token obtained"

    # ── 2.5. RUN PLAYWRIGHT — STAGE 2 ───────────────────────
    log_info "Step 2.5: Running Stage 2 Playwright tests..."

    cd "$FRONTEND_DIR"

    BACKEND_URL="http://127.0.0.1:${LIVE_BACKEND_PORT}" \
    FRONTEND_URL="http://127.0.0.1:${LIVE_FRONTEND_PORT}" \
    E2E_TOKEN="${E2E_TOKEN}" \
    E2E_USERNAME="admin" \
    E2E_PASSWORD="admin123" \
    npx playwright test --grep "Live Project Check" \
        --reporter=list,html \
        || {
            local live_exit=$?
            log_warn "Stage 2 Playwright tests exited with code: $live_exit"
        }

    log_ok "Stage 2 complete!"
}

# ======================================================================
#  MAIN
# ======================================================================

main() {
    echo ""
    echo "╔══════════════════════════════════════════════════════════════╗"
    echo "║     Enterprise Clean E2E Test Orchestrator                  ║"
    echo "║     Release tag: ${RELEASE_TAG}                              ║"
    echo "╚══════════════════════════════════════════════════════════════╝"
    echo ""

    # Check prerequisites
    cd "$PROJECT_ROOT"

    log_info "Prerequisites check..."
    command -v docker >/dev/null 2>&1 || { log_error "docker is required"; return 1; }
    command -v node >/dev/null 2>&1 || { log_error "node is required"; return 1; }
    command -v npm >/dev/null 2>&1 || { log_error "npm is required"; return 1; }
    command -v python3 >/dev/null 2>&1 || { log_error "python3 is required"; return 1; }
    command -v curl >/dev/null 2>&1 || { log_error "curl is required"; return 1; }
    log_ok "All prerequisites met"

    # Install frontend dependencies if needed
    if [[ ! -d "$FRONTEND_DIR/node_modules" ]]; then
        log_info "Installing frontend dependencies..."
        cd "$FRONTEND_DIR"
        npm ci
        cd "$PROJECT_ROOT"
    fi

    # Install Playwright browsers if needed
    if ! npx playwright --version >/dev/null 2>&1; then
        log_info "Installing Playwright..."
        cd "$FRONTEND_DIR"
        npx playwright install chromium
        cd "$PROJECT_ROOT"
    fi

    # ═══════════════════════════════════════════════════════════
    #  STAGE 1: Enterprise Clean Empty Image
    # ═══════════════════════════════════════════════════════════
    log_info "Starting Stage 1: Enterprise Clean Empty Image Setup"
    if ! stage1_enterprise_clean; then
        log_error "Stage 1 failed. Aborting."
        return 1
    fi
    log_ok "Stage 1 completed successfully"

    # ═══════════════════════════════════════════════════════════
    #  STAGE 2: Live Project Verification
    # ═══════════════════════════════════════════════════════════
    log_info "Starting Stage 2: Live Project Verification"
    if ! stage2_live_project; then
        log_error "Stage 2 failed."
        return 1
    fi
    log_ok "Stage 2 completed successfully"

    # ═══════════════════════════════════════════════════════════
    #  COMPLETE
    # ═══════════════════════════════════════════════════════════
    echo ""
    echo "╔══════════════════════════════════════════════════════════════╗"
    echo "║  Enterprise Clean E2E Test Complete!                        ║"
    echo "║                                                             ║"
    echo "║  ✅ Stage 1: Empty Image Setup — Bundle built, deployed,    ║"
    echo "║     StartupEnvironmentWizard verified, environment added.   ║"
    echo "║                                                             ║"
    echo "║  ✅ Stage 2: Live Project — Dev stack verified,             ║"
    echo "║     LLM analysis triggered, settings modified.              ║"
    echo "║                                                             ║"
    echo "║  Bundle: dist/docker/ (tag: ${RELEASE_TAG})                  ║"
    echo "║  Report: frontend/playwright-report/                        ║"
    echo "╚══════════════════════════════════════════════════════════════╝"
    echo ""
}

# Run main
main "$@"
