#!/usr/bin/env bash
# =============================================================================
# run-tests-vm.sh — Lance la stack de test complète dans la VM Rocky Linux 9
#
# Ce script s'exécute DANS la VM (via vagrant ssh ou vagrant provision).
# Il ne peut pas tourner dans Docker — il requiert un vrai kernel pour eBPF.
#
# Usage (depuis le host) :
#   vagrant ssh -- 'bash /ja4-platform/tests/vm/run-tests-vm.sh nginx'
#   vagrant ssh -- 'bash /ja4-platform/tests/vm/run-tests-vm.sh all'
#
# Variables d'environnement :
#   STACK          : stack à tester (nginx|apache|nginx-varnish|hitch-varnish|all)
#   KEEP_RUNNING   : si "true", ne pas arrêter la stack après le test (défaut: false)
# =============================================================================
set -euo pipefail

# S'assurer que /usr/local/bin et go sont dans PATH (nécessaire pour sudo bash)
export PATH="/usr/local/bin:/usr/local/go/bin:$PATH"

STACK="${1:-nginx}"
KEEP_RUNNING="${KEEP_RUNNING:-false}"
PROJECT="/ja4-platform"
RESULTS_DIR="/tmp/ja4-test-results"

# ── Couleurs ─────────────────────────────────────────────────────────────────
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; RESET='\033[0m'
BOLD='\033[1m'

log()  { echo -e "${BOLD}[$STACK]${RESET} $(date +%H:%M:%S) $*"; }
pass() { echo -e "  ${GREEN}✅${RESET}  $*"; ((PASS_COUNT++)) || true; }
fail() { echo -e "  ${RED}❌${RESET}  $*"; ((FAIL_COUNT++)) || true; }
warn() { echo -e "  ${YELLOW}⚠️${RESET}   $*"; ((WARN_COUNT++)) || true; }

PASS_COUNT=0; FAIL_COUNT=0; WARN_COUNT=0

# ── Vérification prérequis ────────────────────────────────────────────────────
check_prerequisites() {
    log "Vérification des prérequis..."
    
    # eBPF capabilities
    if [ ! -d /sys/kernel/tracing ]; then
        fail "tracefs non monté — exécuter: sudo mount -t tracefs tracefs /sys/kernel/tracing"
        exit 1
    fi
    if [ ! -d /sys/kernel/debug ]; then
        fail "debugfs non monté"
        exit 1
    fi
    
    command -v ja4ebpf >/dev/null 2>&1 || {
        log "Rebuild ja4ebpf..."
        cd "$PROJECT/services/ja4ebpf"
        export PATH="/usr/local/go/bin:$PATH"
        GOWORK=off go generate ./internal/loader/ 2>&1 | tail -3
        GOWORK=off CGO_ENABLED=0 go build -o /tmp/ja4ebpf_new ./cmd/ja4ebpf/ && mv /tmp/ja4ebpf_new /usr/local/bin/ja4ebpf
    }
    
    command -v docker >/dev/null 2>&1 || { fail "Docker non installé"; exit 1; }
    command -v nginx >/dev/null 2>&1 || { fail "nginx non installé"; exit 1; }
    pass "Prérequis OK"
}

# ── Démarrage ClickHouse ──────────────────────────────────────────────────────
start_clickhouse() {
    log "Démarrage ClickHouse..."
    
    docker rm -f ja4-clickhouse 2>/dev/null || true
    
    docker run -d --name ja4-clickhouse \
        -p 8123:8123 -p 9000:9000 \
        -e CLICKHOUSE_DB=ja4_processing \
        -e CLICKHOUSE_USER=default \
        -e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 \
        -v "$PROJECT/tests/integration/platform/clickhouse-init.sh:/docker-entrypoint-initdb.d/00_init.sh" \
        $(for f in "$PROJECT/shared/clickhouse/"*.sql; do
            echo "-v $f:/initdb-src/$(basename $f):ro"
          done) \
        clickhouse/clickhouse-server:24.8 2>&1 | tail -1
    
    # Attendre que ClickHouse soit prêt
    log "Attente ClickHouse (max 120s)..."
    for i in $(seq 1 60); do
        if curl -sf "http://localhost:8123/ping" >/dev/null 2>&1; then
            pass "ClickHouse prêt"
            return 0
        fi
        sleep 2
    done
    fail "ClickHouse timeout"; exit 1
}

# ── Configuration nginx ────────────────────────────────────────────────────────
setup_nginx() {
    log "Configuration nginx avec TLS..."
    
    # Certificat auto-signé
    openssl req -x509 -nodes -days 365 \
        -subj "/CN=platform.test" \
        -newkey rsa:2048 \
        -keyout /etc/pki/tls/private/nginx.key \
        -out /etc/pki/tls/certs/nginx.crt 2>/dev/null
    
    # Copier la configuration de test
    cp "$PROJECT/tests/integration/nginx/platform/nginx.conf" /etc/nginx/nginx.conf
    
    # Créer les fichiers de test
    mkdir -p /var/www/html
    # /run/nginx est un tmpfs recréé à chaque boot, nginx en a besoin pour son PID
    mkdir -p /run/nginx
    echo '{"status":"ok","stack":"nginx-vm"}' > /var/www/html/health
    for p in data api/users api/data/test; do
        mkdir -p "/var/www/html/$(dirname $p)"
        echo '{"ok":true}' > "/var/www/html/$p"
    done
    
    nginx -t && nginx
    
    # Attendre nginx
    for i in $(seq 1 20); do
        curl -sf http://localhost/health >/dev/null 2>&1 && break
        sleep 0.5
    done
    pass "nginx démarré"
}

# ── Démarrage ja4ebpf ─────────────────────────────────────────────────────────
start_ja4ebpf() {
    log "Démarrage ja4ebpf..."
    
    pkill ja4ebpf 2>/dev/null || true
    sleep 1
    
    # Créer la config
    cat > /tmp/ja4ebpf.yml << 'EOF'
interface: eth0
ssl_lib_path: "/usr/lib64/libssl.so.3"
clickhouse:
  dsn: "clickhouse://default:@localhost:9000/ja4_logs"
  batch_size: 100
  flush_secs: 1
correlation:
  timeout_ms: 500
  slowloris_ms: 10000
log:
  level: "info"
  format: "json"
EOF
    
    # Lancer avec les capabilities nécessaires
    # Dans la VM (root), on peut lancer directement
    JA4EBPF_CONFIG=/tmp/ja4ebpf.yml ja4ebpf > /tmp/ja4ebpf.log 2>&1 &
    JA4EBPF_PID=$!
    
    sleep 3
    if ! kill -0 "$JA4EBPF_PID" 2>/dev/null; then
        fail "ja4ebpf s'est arrêté immédiatement"
        cat /tmp/ja4ebpf.log | tail -10
        return 1
    fi
    
    log "ja4ebpf démarré (PID $JA4EBPF_PID)"
    
    # Vérifier les uprobes dans tracefs
    sleep 1
    if grep -q "ssl" /sys/kernel/tracing/uprobe_events 2>/dev/null; then
        pass "Uprobes SSL attachés dans tracefs"
    else
        warn "Uprobes non visibles dans tracefs (peuvent être actifs quand même)"
    fi
    
    # Vérifier accept4 tracepoint
    if grep -q "accept4" /sys/kernel/tracing/events/syscalls 2>/dev/null; then
        pass "Tracepoints accept4 disponibles"
    else
        warn "Tracepoints accept4 non trouvés"
    fi
}

# ── Génération de trafic ───────────────────────────────────────────────────────
generate_traffic() {
    log "Génération du trafic (HTTP/1.0 + HTTP/1.1 + HTTP/2)..."
    
    # Trafic HTTP/1.1 (HTTP)
    for path in / /health /data /api/users; do
        curl -sf "http://localhost$path" >/dev/null 2>&1 || true
        curl -sf -X POST "http://localhost/api/data" -d '{"test":1}' >/dev/null 2>&1 || true
    done
    
    # Trafic HTTPS/1.1
    for path in / /health /data /api/users; do
        curl -sf -k "https://localhost$path" >/dev/null 2>&1 || true
        curl -sf -k -X POST "https://localhost/api/data" -d '{"test":1}' >/dev/null 2>&1 || true
        curl -sf -k -X PUT "https://localhost/data" >/dev/null 2>&1 || true
        curl -sf -k -X DELETE "https://localhost/data/1" >/dev/null 2>&1 || true
        curl -sf -k -X HEAD "https://localhost$path" >/dev/null 2>&1 || true
    done
    
    # Trafic HTTP/2
    if command -v python3 >/dev/null 2>&1 && python3 -c "import httpx" 2>/dev/null; then
        python3 << 'PYEOF'
import httpx, ssl, warnings
warnings.filterwarnings("ignore")
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with httpx.Client(http2=True, verify=False) as client:
    for path in ["/", "/health", "/data"]:
        try: client.get(f"https://localhost{path}")
        except: pass
    try: client.post("https://localhost/api/data", json={"test": "h2"})
    except: pass
PYEOF
        pass "Trafic HTTP/2 généré"
    fi
    
    # Attendre le flush ja4ebpf → ClickHouse
    log "Attente flush ja4ebpf (15s)..."
    sleep 15
    pass "Trafic généré"
}

# ── Vérification ClickHouse ────────────────────────────────────────────────────
verify_db() {
    log "Vérification des données dans ClickHouse..."
    
    ch_query() {
        curl -sf "http://localhost:8123/" \
            --data-urlencode "query=$1" \
            --data-urlencode "database=ja4_logs" \
            -o /dev/null -w '%{http_code}' 2>/dev/null || echo "0"
    }
    
    ch_val() {
        curl -sf "http://localhost:8123/?database=ja4_logs" \
            --data-urlencode "query=$1" 2>/dev/null | tr -d ' \n' || echo "0"
    }
    
    # L3/L4
    ttl=$(ch_val "SELECT count() FROM http_logs WHERE ip_meta_ttl > 0")
    [ "${ttl:-0}" -gt 0 ] && pass "L3/L4 TTL capturé ($ttl lignes)" || fail "L3/L4 TTL absent"
    
    mss=$(ch_val "SELECT count() FROM http_logs WHERE tcp_meta_mss > 0")
    [ "${mss:-0}" -gt 0 ] && pass "TCP MSS capturé ($mss lignes)" || fail "TCP MSS absent"
    
    # TLS
    ja4=$(ch_val "SELECT count() FROM http_logs WHERE ja4 != ''")
    [ "${ja4:-0}" -gt 0 ] && pass "JA4 fingerprint capturé ($ja4 lignes)" || fail "JA4 absent"
    
    sni=$(ch_val "SELECT count() FROM http_logs WHERE tls_sni != ''")
    [ "${sni:-0}" -gt 0 ] && pass "TLS SNI capturé ($sni lignes)" || warn "TLS SNI absent"
    
    # L7 HTTP — c'est ici que ça devrait marcher dans la VM
    method=$(ch_val "SELECT count() FROM http_logs WHERE method != ''")
    [ "${method:-0}" -gt 0 ] && pass "L7 méthodes HTTP capturées ($method lignes)" \
                               || fail "L7 méthodes HTTP ABSENT — uprobe SSL_read ne fonctionne pas"
    
    path=$(ch_val "SELECT count() FROM http_logs WHERE path != ''")
    [ "${path:-0}" -gt 0 ] && pass "L7 path HTTP capturé ($path lignes)" || fail "L7 path absent"
    
    status=$(ch_val "SELECT count() FROM http_logs WHERE status_code > 0")
    [ "${status:-0}" -gt 0 ] && pass "status_code capturé ($status lignes)" || warn "status_code absent"
    
    sig=$(ch_val "SELECT count() FROM http_logs WHERE header_order_signature != ''")
    [ "${sig:-0}" -gt 0 ] && pass "header_order_signature capturé ($sig lignes)" || warn "header_order_sig absent"
    
    # Méthodes HTTP distinctes
    methods=$(ch_val "SELECT groupArray(method) FROM (SELECT DISTINCT method FROM http_logs WHERE method != '')")
    log "Méthodes HTTP vues : $methods"
    
    # Lignes totales
    total=$(ch_val "SELECT count() FROM http_logs")
    pass "Total lignes http_logs : $total"
}

# ── Nettoyage ─────────────────────────────────────────────────────────────────
cleanup() {
    if [ "$KEEP_RUNNING" != "true" ]; then
        log "Nettoyage..."
        pkill ja4ebpf 2>/dev/null || true
        nginx -s stop 2>/dev/null || true
        docker rm -f ja4-clickhouse 2>/dev/null || true
    fi
}
trap cleanup EXIT

# ── Main ──────────────────────────────────────────────────────────────────────
mkdir -p "$RESULTS_DIR"

echo ""
echo "╔══════════════════════════════════════════╗"
echo "║  ja4ebpf VM Test Suite — Rocky Linux 9   ║"
echo "╚══════════════════════════════════════════╝"
echo ""

check_prerequisites
start_clickhouse
setup_nginx
start_ja4ebpf
generate_traffic
verify_db

echo ""
echo "════════════════════════════════════════════"
echo -e "  ${GREEN}OK${RESET}: $PASS_COUNT  ${YELLOW}WARN${RESET}: $WARN_COUNT  ${RED}FAIL${RESET}: $FAIL_COUNT"
if [ "$FAIL_COUNT" -eq 0 ]; then
    echo -e "  ${GREEN}${BOLD}Tous les tests réussis !${RESET}"
    exit 0
else
    echo -e "  ${RED}${BOLD}$FAIL_COUNT tests échoués.${RESET}"
    echo "Logs ja4ebpf :"
    tail -20 /tmp/ja4ebpf.log 2>/dev/null || true
    exit 1
fi
