#!/bin/sh
# bullpen-roster-watch — alle 13 min Soll==Ist prueft und NUR BEI AENDERUNG
# der Abweichungslage eine Mail an den Menschen schickt.
#
# WOZU (markus, Issue #6, 2026-08-14): "Agenten basteln gerne hands on, ohne
# Reprodutivitaet sicher zu stellen." bullpen-verify macht Drift sichtbar; der
# stuendliche @doctor meldet sie IN DEN RAUM. Dieser Waechter meldet sie dem
# MENSCHEN per Mail — aber nur, wenn sich die Lage AENDERT. Bei ~110 Laeufen am
# Tag waere eine Mail je Lauf mit stehender Abweichung Spam, und Spam wird
# ignoriert, und dann ist der Waechter fuer die Katz. Deshalb: die
# Abweichungs-SIGNATUR (welche Pruefungen rot, nicht deren Reihenfolge) wird
# gegen den letzten Stand verglichen; Mail nur bei sauber->rot, rot->sauber,
# oder rot->ANDERS-rot.
set -u

VERIFY="${BULLPEN_VERIFY:-/usr/bin/bullpen-verify}"
# State liegt auf dem VERIFY-Wirt (hertz), NICHT im room-Container — hertz hat
# kein /var/lib/bullpen. Eigenes Verzeichnis, vom Waechter selbst angelegt,
# damit ein Handlauf (--once) ohne die systemd-StateDirectory nicht scheitert.
STATE="${BULLPEN_ROSTER_WATCH_STATE:-/var/lib/bullpen-roster-watch/state.sig}"
mkdir -p "$(dirname "$STATE")" 2>/dev/null || true
MAILTO="${BULLPEN_ROSTER_WATCH_MAILTO:-mfritsche@reauktion.de}"
MAILFROM="${BULLPEN_ROSTER_WATCH_FROM:-automation@reauktion.de}"
SENDMAIL="${BULLPEN_SENDMAIL:-/usr/sbin/sendmail}"

out=$("$VERIFY" --json 2>/dev/null)
rc=$?

# Kann der Waechter nicht messen (Wirt/Container weg, verify-Absturz), ist das
# selbst ein meldenswerter Zustand — aber genau EINMAL, nicht alle 13 min.
if [ $rc -ge 2 ] || [ -z "$out" ]; then
    sig="PROBE-FAILED rc=$rc"
    body="bullpen-roster-watch konnte nicht messen: bullpen-verify rc=$rc, keine JSON-Ausgabe. Wirt oder room-Container pruefen."
else
    # Signatur = sortierte Liste der fail-ids, plus die counts. Reihenfolge im
    # Envelope ist stabil, aber wir sortieren, damit ein reines Umordnen keine
    # Falschmeldung ausloest.
    fails=$(printf '%s' "$out" | jq -r '.checks[] | select(.status=="fail") | .id' 2>/dev/null | LC_ALL=C sort | tr '\n' '|')
    counts=$(printf '%s' "$out" | jq -r '"\(.counts.pass)/\(.total)"' 2>/dev/null)
    if [ -z "$fails" ]; then
        sig="CLEAN $counts"
    else
        sig="DRIFT $counts :: $fails"
    fi
    body=$(printf '%s' "$out" | jq -r '
        "bullpen-Roster-Drift, Stand \(.generated_at):",
        "  \(.counts.pass)/\(.total) Pruefungen gruen, \(.counts.fail) Abweichung(en).",
        "",
        (.checks[] | select(.status=="fail") | "  [!!] \(.id): \(.deviation)"),
        "",
        "Quelle ist der roster (deploy/roster.yaml) gegen den laufenden Aufbau.",
        "Entscheiden, nicht wegklicken: entweder der roster luegt oder es ist gedriftet."
    ' 2>/dev/null)
fi

# ENTPRELLEN gegen den instabilen Messkanal. bullpen-verify fragt die
# Container ueber `sic hertz` (ssh zu sich selbst) ab; unter Contention
# timeouten die Aufrufe sporadisch, ein ganzer Schwung "existiert und laeuft"
# faellt gleichzeitig auf "-", und ein EINZELNER Lauf meldet dann DRIFT, wo
# keine ist (gemessen 2026-08-14: 93/93 und 1/16 im Minutenabstand). Der
# Beobachter muss gegen seinen eigenen Kanal robust sein: gemeldet wird erst,
# wenn ZWEI aufeinanderfolgende Laeufe DIESELBE neue Signatur zeigen.
# Zwei Marken: CONFIRMED (zuletzt gemeldeter Stand) + PENDING (Kandidat).
CONFIRMED="$STATE"
PENDING="${STATE%.sig}.pending"

confirmed=""; [ -f "$CONFIRMED" ] && confirmed=$(cat "$CONFIRMED" 2>/dev/null)
pending="";   [ -f "$PENDING" ]   && pending=$(cat "$PENDING" 2>/dev/null)

# Stabiler, schon gemeldeter Stand -> stumm, Kandidat verwerfen.
if [ "$sig" = "$confirmed" ]; then
    rm -f "$PENDING" 2>/dev/null
    exit 0
fi

# Neue Signatur, aber noch nicht bestaetigt: als Kandidat merken, NICHT melden.
if [ "$sig" != "$pending" ]; then
    printf '%s' "$sig" > "$PENDING" 2>/dev/null
    exit 0
fi

# sig == pending: zweiter Lauf bestaetigt die Aenderung -> jetzt uebernehmen.
printf '%s' "$sig" > "$CONFIRMED" 2>/dev/null
rm -f "$PENDING" 2>/dev/null

# Grundlinie sauber (kein voriger confirmed-Stand, jetzt CLEAN) = keine
# Neuigkeit, nur der Startpunkt.
if [ -z "$confirmed" ]; then
    case "$sig" in
        CLEAN*) exit 0 ;;
    esac
fi

subj="[bullpen] Roster-Drift: $sig"
{
    printf 'From: %s\n' "$MAILFROM"
    printf 'To: %s\n' "$MAILTO"
    printf 'Subject: %s\n' "$subj"
    printf '\n%s\n' "$body"
} | "$SENDMAIL" -f "$MAILFROM" "$MAILTO"
