#!/usr/bin/env bash
# End-to-end smoke check.
#
# Mirrors the README quickstart: boots the compose stack, creates an
# admin token, creates a reporter + consumer + their tokens, posts a
# report, triggers the recompute job, pulls the blocklist, and asserts
# the IP shows up. Tears down at the end.
#
# Run from the repo root:
#     ./tests/e2e/demo.sh
#
# Requirements: Docker. No PHP/Node/Composer needed on the host.
# Expected runtime: ~90 seconds (most of it the FrankenPHP cold start).
#
# Exits non-zero on any step failure. Sets `set -e` so the first error
# aborts; `trap` ensures the stack is torn down even on early exit.
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$REPO_ROOT"

LOG_PREFIX="\033[1;36m[e2e]\033[0m"
log()  { printf "%b %s\n" "$LOG_PREFIX" "$*"; }
fail() { printf "\033[1;31m[fail]\033[0m %s\n" "$*" >&2; exit 1; }

cleanup() {
    log "tearing down compose stack"
    docker compose down -v >/dev/null 2>&1 || true
}
trap cleanup EXIT

# ---- Step 1: prepare a clean .env ---------------------------------
log "preparing .env from .env.example"
cp .env.example .env

# Generate a deterministic local-admin password hash so the test is
# reproducible. The helper escape doubles every $ so docker-compose
# variable substitution doesn't eat them.
HASH=$(php -r "echo password_hash('e2e-demo-password', PASSWORD_ARGON2ID);" | sed 's/\$/$$/g')

# Fill in the secrets needed for boot. Use a small awk to substitute
# in place — POSIX sed's behaviour for "key=" lines varies across
# distros for special chars in the value.
awk -v hash="$HASH" '
    BEGIN { ui_secret=ui_svc=int_tok=app_secret="" }
    /^UI_SECRET=/      { print "UI_SECRET="                            "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; next }
    /^APP_SECRET=/     { print "APP_SECRET="                           "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"; next }
    /^INTERNAL_JOB_TOKEN=/ { print "INTERNAL_JOB_TOKEN="               "abababababababababababababababababababababababababababababababab"; next }
    /^UI_SERVICE_TOKEN=/   { print "UI_SERVICE_TOKEN="                 "irdb_svc_AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD"; next }
    /^OIDC_ENABLED=/   { print "OIDC_ENABLED=false"; next }
    /^LOCAL_ADMIN_PASSWORD_HASH=/ { print "LOCAL_ADMIN_PASSWORD_HASH=" hash; next }
    { print }
' .env > .env.tmp && mv .env.tmp .env

# ---- Step 2: bring the stack up ----------------------------------
log "compose up"
docker compose up -d --build >/dev/null

log "waiting for api healthcheck (up to 60s)"
for _ in $(seq 1 60); do
    if curl -sf http://localhost:8081/healthz >/dev/null 2>&1; then
        break
    fi
    sleep 1
done
curl -sf http://localhost:8081/healthz >/dev/null || fail "api never became healthy"

log "waiting for ui healthcheck (up to 30s)"
for _ in $(seq 1 30); do
    if curl -sf http://localhost:8080/healthz >/dev/null 2>&1; then
        break
    fi
    sleep 1
done
curl -sf http://localhost:8080/healthz >/dev/null || fail "ui never became healthy"

# ---- Step 3: bootstrap the service token + admin token -----------
log "bootstrapping the UI service token row"
docker compose exec -T api php bin/console auth:bootstrap-service-token >/dev/null

log "creating an admin token via CLI"
ADMIN_TOKEN=$(docker compose exec -T api php bin/console \
    auth:create-token --kind=admin --role=admin --quiet | tr -d '\r')
[ -n "$ADMIN_TOKEN" ] || fail "no admin token returned"

# ---- Step 4: create a reporter + reporter token ------------------
log "creating reporter + token"
RID=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name":"e2e-rep","trust_weight":1.0}' \
    http://localhost:8081/api/v1/admin/reporters \
    | php -r 'echo json_decode(stream_get_contents(STDIN),true)["id"];')
[ -n "$RID" ] || fail "no reporter id returned"

REP_TOKEN=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"kind\":\"reporter\",\"reporter_id\":$RID}" \
    http://localhost:8081/api/v1/admin/tokens \
    | php -r 'echo json_decode(stream_get_contents(STDIN),true)["raw_token"];')
[ -n "$REP_TOKEN" ] || fail "no reporter token returned"

# ---- Step 5: post some reports ----------------------------------
log "submitting reports for 203.0.113.10..12"
for i in 10 11 12; do
    curl -sf -X POST -H "Authorization: Bearer $REP_TOKEN" \
        -H "Content-Type: application/json" \
        -d "{\"ip\":\"203.0.113.$i\",\"category\":\"brute_force\"}" \
        http://localhost:8081/api/v1/report > /dev/null
done

# ---- Step 6: create a consumer + consumer token -----------------
log "creating consumer + token"
# Pick policy id 1 (the seeded "moderate" policy).
CID=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name":"e2e-con","policy_id":1}' \
    http://localhost:8081/api/v1/admin/consumers \
    | php -r 'echo json_decode(stream_get_contents(STDIN),true)["id"];')
[ -n "$CID" ] || fail "no consumer id returned"

CON_TOKEN=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"kind\":\"consumer\",\"consumer_id\":$CID}" \
    http://localhost:8081/api/v1/admin/tokens \
    | php -r 'echo json_decode(stream_get_contents(STDIN),true)["raw_token"];')
[ -n "$CON_TOKEN" ] || fail "no consumer token returned"

# ---- Step 7: trigger recompute via admin endpoint ---------------
log "triggering recompute-scores"
RESP=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
    -H "Content-Type: application/json" -d '{"full":true}' \
    http://localhost:8081/api/v1/admin/jobs/trigger/recompute-scores)
echo "$RESP" | grep -q '"status":"success"' || fail "recompute did not succeed: $RESP"

# ---- Step 8: pull the blocklist; assert non-empty ----------------
log "pulling blocklist"
LIST=$(curl -sf -H "Authorization: Bearer $CON_TOKEN" http://localhost:8081/api/v1/blocklist)
COUNT=$(printf '%s\n' "$LIST" | grep -cE '^[0-9]' || true)
[ "$COUNT" -ge 1 ] || fail "blocklist empty (count=$COUNT)"
log "blocklist has $COUNT entries"

# ---- Step 9: poke the OpenAPI viewer -----------------------------
log "fetching /api/v1/openapi.yaml"
curl -sf http://localhost:8081/api/v1/openapi.yaml | grep -q '^openapi:' \
    || fail "openapi.yaml missing or unexpected"

log "fetching /api/docs"
curl -sf http://localhost:8081/api/docs | grep -qi 'rapi-doc' \
    || fail "/api/docs did not render the viewer"

log "all checks passed"
