#!/usr/bin/env bash
# Ampel-Wächter (22.07.2026, Wasserdicht-Runde): meldet ROTE CI-Läufe aller
# Gitea-Repos per Telegram. Läuft als no-agent-Cron (stdout leer = still,
# klassisches Watchdog-Muster). Nur NEUE Fehlschläge werden gemeldet —
# Status-Merkliste unter ~/.hermes/state/ampel-waechter-gesehen.txt.
set -uo pipefail

STATE="$HOME/.hermes/state/ampel-waechter-gesehen.txt"
mkdir -p "$(dirname "$STATE")"; touch "$STATE"

TOKEN=$(grep -m1 'git\.tobisniceshomelab' "$HOME/.git-credentials" 2>/dev/null | sed -E 's#https://[^:]+:([^@]+)@.*#\1#')
[ -n "$TOKEN" ] || exit 0

python3 - "$TOKEN" "$STATE" <<'PY'
import json, sys, urllib.request, subprocess, os

token, state_pfad = sys.argv[1], sys.argv[2]
# API intern (die DDNS-Domain ist nachts wegen Zwangstrennung oft tot, 24.07.2026);
# WEB bleibt die Domain — Telegram-Links muessen auch von unterwegs klickbar sein.
BASE = "http://192.168.178.153:3000/api/v1"
WEB = "https://git.tobisniceshomelab.ddnsfree.com"


def api(pfad):
    req = urllib.request.Request(BASE + pfad, headers={"Authorization": "token " + token})
    with urllib.request.urlopen(req, timeout=20) as r:
        return json.load(r)


try:
    gesehen = set(open(state_pfad).read().split())
except OSError:
    gesehen = set()

neu = []
try:
    # /repos/search statt /user/repos: das Token hat nur write:repository-Scope,
    # /user/repos verlangt read:user -> 403. Der stille exit 0 hat diesen Defekt
    # seit Inbetriebnahme versteckt (Fund 24.07.2026, Merkliste blieb leer).
    repos = api("/repos/search?limit=50&private=true").get("data", [])
except Exception as e:
    print(f"ampel-waechter: Repo-Liste fehlgeschlagen: {e}", file=sys.stderr)
    sys.exit(0)

for repo in repos:
    full = repo.get("full_name", "")
    if not full or repo.get("archived"):
        continue
    try:
        d = api(f"/repos/{full}/actions/tasks?limit=1")
    except Exception:
        continue
    rows = d.get("workflow_runs", d if isinstance(d, list) else d.get("entries", []))
    for t in rows[:1]:
        kennung = f"{full}#{t.get('id')}"
        status = t.get("status")
        if status == "failure" and kennung not in gesehen:
            # Status-Lügen-Wache (Fund 22.07.): alter Runner meldete Abschluss nicht,
            # Gitea zombie-markierte GRÜNE Jobs als failure. Das Job-LOG ist die
            # Wahrheit — steht dort „Job succeeded", ist es KEIN Alarm.
            try:
                req = urllib.request.Request(
                    f"{BASE}/repos/{full}/actions/jobs/{t.get('id')}/logs",
                    headers={"Authorization": "token " + token})
                with urllib.request.urlopen(req, timeout=20) as r:
                    log_ende = r.read().decode(errors="replace")[-4000:]
                wirklich_rot = "Job succeeded" not in log_ende
            except Exception:
                wirklich_rot = True
            if wirklich_rot:
                neu.append((full, t.get("head_sha", "")[:7], t.get("name", "")))
        # Nur ABGESCHLOSSENE Läufe merken — ein 'running', das später rot wird,
        # darf nicht schon als gesehen gelten.
        if status in ("failure", "success", "cancelled", "skipped"):
            gesehen.add(kennung)

if neu:
    print("🔴 CI-AMPEL ROT:")
    for full, sha, name in neu:
        print(f"• {full} @ {sha} ({name})")
        print(f"  {WEB}/{full}/actions")
        try:
            subprocess.run([
                os.path.expanduser("~/.local/bin/hermes"), "kanban", "create", 
                "--title", f"CI rot: {full} @ {sha}", 
                "--body", f"Der CI Lauf '{name}' in {full} ist fehlgeschlagen. Bitte Log ansehen und Fix vorschlagen."
            ], capture_output=True)
        except Exception as e:
            print(f"Fehler bei kanban_create: {e}", file=sys.stderr)
    print("Nichts aus diesen Ständen ist ‚fertig', solange die Ampel rot ist.")

with open(state_pfad, "w") as f:
    f.write("\n".join(sorted(gesehen)))
PY
