#!/usr/bin/env bash
# Live M6 smoke test. Run from repo root:
#   bash scripts/m6_smoke.sh
#
# Walks through the 5 scenarios in M6_VERIFICATION.md:
#
#   Step 2 — sliding-window ×N: 5 identical alerts → recipient sees
#            the (×5) suffix on the 5th message
#   Step 3 — dedupe_count metric: max-observed gauge and
#            dedupe_collapsed counter both tick
#   Step 4 — sliding TTL: a duplicate after > TTL is a fresh "new"
#   Step 5 — free-for-dupes: 200 dupes/s never trigger the rate limit
#            (per-source cap is 100/s, but only the first alert burns
#            a token; the other 199 are free)
#   Step 6 — per-source key isolation: same dedupe_key from a
#            different source does NOT collide
#
# Exit code is the number of failed checks.

set -e
cd "$(dirname "$0")/.."

PG="docker exec -i broad-announce-postgres-1 psql -U ba -d ba -A -t"
INGESTD_METRICS=http://localhost:8800/metrics
FAKETGMD=http://localhost:8830
WS_INGEST=ws://localhost:8800/v1/ingest/ws
SRC_ACME=acme-001:prom-prod:s3cret-acme
SRC_GLOBEX=globex-002:grafana:s3cret-globex

fails=0
pass() { echo "  ✅ $*"; }
fail() { echo "  ❌ $*"; fails=$((fails+1)); }

reset_state() {
  $PG -c "UPDATE individuals SET telegram_chat_id = NULL, telegram_user_id = NULL, telegram_invite_code = 'acme-bob-002' WHERE id = 'ind-acme-002';" >/dev/null
  $PG -c "UPDATE individuals SET telegram_chat_id = NULL, telegram_user_id = NULL WHERE id = 'ind-acme-003';" >/dev/null
  $PG -c "UPDATE subscriptions SET min_severity = 'critical' WHERE individual_id = 'ind-acme-002' AND source_id = 'prom-prod';" >/dev/null
  $PG -c "TRUNCATE deliveries;" >/dev/null
  curl -sS -X POST $FAKETGMD/admin/reset >/dev/null
}

# Per-source dedupe metric readers.
dedupe_collapsed() {
  # $1 = source_id
  curl -sS "$INGESTD_METRICS" 2>/dev/null | \
    grep -E "^ba_ingestd_dedupe_collapsed_total\{[^}]*source=\"$1\"" | \
    awk '{print $NF}' | awk -F. '{print $1+0; exit}' | head -1
}

dedupe_max() {
  # $1 = source_id
  curl -sS "$INGESTD_METRICS" 2>/dev/null | \
    grep -E "^ba_ingestd_dedupe_count_max_observed\{[^}]*source=\"$1\"" | \
    awk '{print $NF}' | awk -F. '{print $1+0; exit}' | head -1
}

ws_messages() {
  # $1 = result label
  curl -sS "$INGESTD_METRICS" 2>/dev/null | \
    grep -E "^ba_ingestd_ws_messages_total\{result=\"$1\"" | \
    awk '{print $NF}' | awk -F. '{print $1+0; exit}' | head -1
}

# ── Setup: build loadgen-ws ──
if [[ ! -x /tmp/loadgen-ws-m6 ]]; then
  echo "▸ Building /tmp/loadgen-ws-m6"
  (cd loadgen && CGO_ENABLED=0 go build -o /tmp/loadgen-ws-m6 ./cmd/ws)
fi

# ─────────────────────────────────────────────────────────────────
echo "── M6 smoke — Dedupe + ×N suffix + sliding TTL + free-for-dupes ──"
echo ""

# ── Step 2: ×N suffix on the 5th message ───────────────────
echo "── Step 2: 5 identical alerts → (×5) suffix on the last ──"
reset_state
sleep 1  # let the dedupe window drain
KEY="m6-step2-$(date +%s%N)"
# We use a unique key per run so the dedupe count starts at 1.
/tmp/loadgen-ws-m6 --target "$WS_INGEST" --api-key "$SRC_ACME" --count 5 --rate 10 --dedupe-key "$KEY" 2>&1 | tail -2
sleep 3

# Read the last faketgmd message; expect "(×5)" in the title.
last_text=$(curl -sS $FAKETGMD/admin/sent 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); items=d.get("items",[]); print(items[-1]["text"] if items else "")' 2>/dev/null)
if echo "$last_text" | grep -q '(×5)'; then
  pass "faketgmd last message contains '(×5)'"
else
  fail "faketgmd last message: $last_text (expected '(×5)' suffix)"
fi

# ── Step 3: metrics tick correctly ────────────────────────
echo ""
echo "── Step 3: dedupe_collapsed and dedupe_count_max_observed both tick ──"
collapsed_before=$(dedupe_collapsed prom-prod)
max_before=$(dedupe_max prom-prod)
collapsed_before=${collapsed_before:-0}
max_before=${max_before:-0}

KEY="m6-step3-$(date +%s%N)"
/tmp/loadgen-ws-m6 --target "$WS_INGEST" --api-key "$SRC_ACME" --count 5 --rate 10 --dedupe-key "$KEY" 2>&1 | tail -2
sleep 2

collapsed_after=$(dedupe_collapsed prom-prod)
max_after=$(dedupe_max prom-prod)
collapsed_after=${collapsed_after:-0}
max_after=${max_after:-0}

collapsed_delta=$((collapsed_after - collapsed_before))
max_delta=$((max_after - max_before))

if [[ $collapsed_delta -ge 4 ]]; then
  pass "dedupe_collapsed_total{source=\"prom-prod\"} +$collapsed_delta (≥4 of 5 are dupes)"
else
  fail "dedupe_collapsed_total delta was $collapsed_delta (expected ≥4)"
fi
if [[ $max_after -ge 5 ]]; then
  pass "dedupe_count_max_observed{source=\"prom-prod\"} = $max_after (≥5)"
else
  fail "dedupe_count_max_observed was $max_after (expected ≥5)"
fi

# ── Step 4: sliding TTL — a fresh dedupe_key is "new" ──────
echo ""
echo "── Step 4: fresh dedupe_key (no collision with prior burst) → all accepted as new ──"
reset_state
sleep 1
# Use a brand new key; the loadgen's normal profile would also
# pick a unique key per alert, but to keep this deterministic
# we use --dedupe-key with a timestamp.
KEY="m6-step4-$(date +%s%N)"
recv_before=$(ws_messages received)
acc_before=$(ws_messages accepted)

/tmp/loadgen-ws-m6 --target "$WS_INGEST" --api-key "$SRC_ACME" --count 3 --rate 5 --dedupe-key "$KEY" 2>&1 | tail -2
sleep 2

recv_after=$(ws_messages received)
acc_after=$(ws_messages accepted)
recv_delta=$((recv_after - recv_before))
acc_delta=$((acc_after - acc_before))

if [[ $recv_delta -eq 3 ]]; then
  pass "ws_messages_total{result=\"received\"} +$recv_delta"
else
  fail "ws_messages_total{result=\"received\"} delta was $recv_delta (expected 3)"
fi
if [[ $acc_delta -eq 3 ]]; then
  pass "ws_messages_total{result=\"accepted\"} +$acc_delta (all 3 new, fresh key)"
else
  fail "ws_messages_total{result=\"accepted\"} delta was $acc_delta (expected 3)"
fi

# ── Step 5: free-for-dupes — rate limit never fires on a dupe storm ──
echo ""
echo "── Step 5: 200 dupes/s (10× source rate limit) → no rate_limited_source ──"
# The default per-source rate is 100/s in docker-compose. We
# blast 300 dupes/s (3× over) for 2 seconds and confirm the
# rate_limited_source counter doesn't tick.
reset_state
rl_before=$(curl -sS "$INGESTD_METRICS" 2>/dev/null | grep -E '^ba_ingestd_rate_limit_hits_total\{[^}]*scope="source"' | awk '{print $NF}' | awk -F. '{print $1+0; exit}' | head -1)
rl_before=${rl_before:-0}
ws_acc_before=$(ws_messages accepted)

KEY="m6-step5-$(date +%s%N)"
/tmp/loadgen-ws-m6 --target "$WS_INGEST" --api-key "$SRC_ACME" --count 600 --rate 300 --dedupe-key "$KEY" 2>&1 | tail -2
sleep 3

rl_after=$(curl -sS "$INGESTD_METRICS" 2>/dev/null | grep -E '^ba_ingestd_rate_limit_hits_total\{[^}]*scope="source"' | awk '{print $NF}' | awk -F. '{print $1+0; exit}' | head -1)
ws_acc_after=$(ws_messages accepted)
rl_delta=$((rl_after - rl_before))
accepted_delta=$((ws_acc_after - ws_acc_before))

if [[ $rl_delta -eq 0 ]]; then
  pass "rate_limit_hits_total{scope=\"source\"} did not tick (free-for-dupes works)"
else
  fail "rate_limit_hits_total{scope=\"source\"} +$rl_delta (expected 0)"
fi
if [[ $accepted_delta -ge 500 ]]; then
  pass "ws_messages_total{result=\"accepted\"} +$accepted_delta (≥500 of 600 accepted)"
else
  fail "ws_messages_total{result=\"accepted\"} +$accepted_delta (expected ≥500)"
fi

# ── Step 6: per-source isolation ─────────────────────────
echo ""
echo "── Step 6: same dedupe_key from a different source does NOT collide ──"
reset_state
sleep 1
KEY="m6-step6-$(date +%s%N)"
globex_max_before=$(dedupe_max grafana)
globex_max_before=${globex_max_before:-0}

# Send 3 from acme, 3 from globex, all with the same key.
# Expect: acme counts 1,2,3; globex counts 1,2,3 (no cross-pollination).
/tmp/loadgen-ws-m6 --target "$WS_INGEST" --api-key "$SRC_ACME" --count 3 --rate 5 --dedupe-key "$KEY" 2>&1 | tail -2
sleep 1
/tmp/loadgen-ws-m6 --target "$WS_INGEST" --api-key "$SRC_GLOBEX" --count 3 --rate 5 --dedupe-key "$KEY" 2>&1 | tail -2
sleep 2

globex_max_after=$(dedupe_max grafana)
globex_max_after=${globex_max_after:-0}

if [[ $globex_max_after -ge 3 ]]; then
  pass "dedupe_count_max_observed{source=\"grafana\"} = $globex_max_after (≥3 from globex)"
else
  fail "dedupe_count_max_observed{source=\"grafana\"} was $globex_max_after (expected ≥3)"
fi

# Also: acme's max should be ≥3 but not affected by globex's hits
# (it should be the same as it was after Step 5's storm). We
# don't assert a delta here — just that the gauge is non-zero.
acme_max=$(dedupe_max prom-prod)
acme_max=${acme_max:-0}
if [[ $acme_max -ge 1 ]]; then
  pass "dedupe_count_max_observed{source=\"prom-prod\"} = $acme_max (per-source isolation preserved)"
else
  fail "dedupe_count_max_observed{source=\"prom-prod\"} was $acme_max (expected ≥1)"
fi

# ── Summary ─────────────────────────────────────────────────
echo ""
if [[ $fails -eq 0 ]]; then
  echo "🟢 M6 smoke PASS — all checks green"
  exit 0
else
  echo "🔴 M6 smoke FAIL — $fails check(s) failed"
  exit $fails
fi
