#!/usr/bin/env bash
# Both branches per verdict: a run that really worked must pass, and each way a
# dispatch can die silently must be caught.
set -uo pipefail

GUARD="$(cd "$(dirname "$0")/../bin" && pwd)/dispatch-guard"
PASS=0; FAIL=0

check() {
  local name="$1" want="$2" got="$3"
  if [ "$want" = "$got" ]; then PASS=$((PASS+1)); printf 'ok   %s\n' "$name"
  else FAIL=$((FAIL+1)); printf 'FAIL %s: want=%s got=%s\n' "$name" "$want" "$got"; fi
}

WORK=$(mktemp -d); trap 'rm -rf "$WORK"' EXIT
export DISPATCH_GUARD_STATE="$WORK/state"
LEDGER="$DISPATCH_GUARD_STATE/dispatches.jsonl"

verdict_of() { tail -1 "$LEDGER" | sed -n 's/.*"verdict":"\([^"]*\)".*/\1/p'; }

# --- a real run: long enough, enough output, clean exit ---
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=10 \
  "$GUARD" good "$WORK/good.out" -- bash -c 'printf "%0.sx" {1..64}' >/dev/null 2>&1
check "real run exits 0" 0 $?
check "real run recorded OK" OK "$(verdict_of)"

# --- the 16-corpse case: wrapper error, tiny output, instant exit ---
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=1024 \
  "$GUARD" tiny "$WORK/tiny.out" -- bash -c 'echo "cdx: containment unavailable — mirror push failed"' >/dev/null 2>&1
check "tiny output exits 1" 1 $?
check "tiny output recorded NO-OUTPUT" NO-OUTPUT "$(verdict_of)"

# --- a zero exit does NOT clear an empty transcript ---
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=1024 \
  "$GUARD" zeroexit "$WORK/zero.out" -- true >/dev/null 2>&1
check "zero exit with no output still fails" 1 $?
check "zero exit with no output recorded NO-OUTPUT" NO-OUTPUT "$(verdict_of)"

# --- died before it could have reached a model ---
DISPATCH_GUARD_MIN_RUNTIME=30 DISPATCH_GUARD_MIN_BYTES=1 \
  "$GUARD" fast "$WORK/fast.out" -- bash -c 'printf "%0.sx" {1..4096}' >/dev/null 2>&1
check "too-fast run exits 1" 1 $?
check "too-fast run recorded DIED-EARLY" DIED-EARLY "$(verdict_of)"

# --- a genuine non-zero exit after real work ---
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=10 \
  "$GUARD" broke "$WORK/broke.out" -- bash -c 'printf "%0.sx" {1..64}; exit 3' >/dev/null 2>&1
check "failing run exits 1" 1 $?
check "failing run recorded FAILED" FAILED "$(verdict_of)"

# --- the verdict is loud on stderr, not only in the ledger ---
err=$(DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=1024 \
  "$GUARD" loud "$WORK/loud.out" -- bash -c 'echo hi' 2>&1 >/dev/null)
case "$err" in *"DISPATCH loud: NO-OUTPUT"*) check "verdict printed loudly" yes yes;;
  *) check "verdict printed loudly" yes no;; esac

# --- the ledger survives a slug and detail carrying quotes and newlines ---
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=1024 \
  "$GUARD" 'we"ird' "$WORK/odd.out" -- bash -c 'printf "a\"b\nc\td\n"' >/dev/null 2>&1
if command -v python3 >/dev/null; then
  python3 -c 'import json,sys; [json.loads(l) for l in open(sys.argv[1]) if l.strip()]' "$LEDGER" 2>/dev/null
  check "every ledger line is valid json" 0 $?
fi

# --- status reports the LAST verdict per slug, not every attempt ---
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=10 \
  "$GUARD" tiny "$WORK/tiny.out" -- bash -c 'printf "%0.sx" {1..64}' >/dev/null 2>&1
status=$("$GUARD" status)
case "$status" in *"OK          tiny"*) check "status shows the retry, not the old failure" yes yes;;
  *) check "status shows the retry, not the old failure" yes no;; esac
case "$status" in *"NO-OUTPUT   zeroexit"*) check "status still shows other slugs' failures" yes yes;;
  *) check "status still shows other slugs' failures" yes no;; esac

DISPATCH_GUARD_STATE="$WORK/empty" "$GUARD" status >/dev/null 2>&1
check "status on an empty ledger exits 0" 0 $?

# --- the run is visible in Overdeck, both while running and at its verdict ---
export DISPATCH_GUARD_JOURNAL="$WORK/items.jsonl"
: > "$DISPATCH_GUARD_JOURNAL"
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=10 \
  "$GUARD" seen "$WORK/seen.out" -- bash -c 'printf "%0.sx" {1..64}' >/dev/null 2>&1
check "publishes RUNNING then the verdict" 2 "$(grep -c '"id":"dispatch:seen"' "$DISPATCH_GUARD_JOURNAL")"
check "an OK run publishes as info" 1 "$(grep -c '"severity":"info".*seen OK' "$DISPATCH_GUARD_JOURNAL")"

: > "$DISPATCH_GUARD_JOURNAL"
DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=1024 \
  "$GUARD" seenbad "$WORK/seenbad.out" -- bash -c 'echo hi' >/dev/null 2>&1
check "a failed run publishes as act/alert" 1 "$(grep -c '"severity":"act","kind":"alert".*seenbad NO-OUTPUT' "$DISPATCH_GUARD_JOURNAL")"
python3 -c 'import json,sys; [json.loads(l) for l in open(sys.argv[1]) if l.strip()]' "$DISPATCH_GUARD_JOURNAL" 2>/dev/null
check "published rows are valid json" 0 $?

# a missing journal directory must never take the dispatch down with it
DISPATCH_GUARD_JOURNAL="$WORK/nodir/items.jsonl" DISPATCH_GUARD_MIN_RUNTIME=0 DISPATCH_GUARD_MIN_BYTES=10 \
  "$GUARD" nojournal "$WORK/nj.out" -- bash -c 'printf "%0.sx" {1..64}' >/dev/null 2>&1
check "an unwritable journal does not fail the run" 0 $?
unset DISPATCH_GUARD_JOURNAL

# --- usage errors are distinguishable from a failed dispatch ---
"$GUARD" onlyslug >/dev/null 2>&1
check "missing args exit 2" 2 $?

printf '\n%s passed, %s failed\n' "$PASS" "$FAIL"
[ "$FAIL" -eq 0 ]
