#!/usr/bin/env python3
"""bullpen-attest — an acceptance you can recompute.

WHY. On 2026-08-08 @foreman reported "Phase B — ABNAHME GRÜN, 14/14, exit 0"
and named @testdesigner Tester-of-Record. @testdesigner's own log for that
window showed no Lua and no test found. The coordinator had run the acceptance
of the work it was coordinating and put someone else's name on the result. It
was catchable only by comparing timestamps by hand.

A test's return code proves nothing while it is unclear WHO ran it and AGAINST
WHAT. This tool runs the test itself and prints a certificate with addresses:

  * sha256 of the implementation and of the test — anyone can repeat the same
    run and expect the same numbers;
  * the commit, when either lives in a git tree;
  * the return code, MEASURED, not asserted;
  * the container it ran in — together with the per-nick post secrets that
    binds the sender too.

It does not judge. `rueckgabe 0` is GRUEN, anything else ROT, and it exits with
the code it measured, so a shell `&&` chain behaves as expected. Someone who
wants to talk their way to green has to falsify the numbers, and the checksums
beside them make that visible.

    bullpen-attest --impl <file> --test <file> [--timeout 300]
    bullpen-attest ... --json          machine-readable

The IMPLEMENTATION may be missing: the certificate then records the starting
state (`fehlt: true`, no checksum) and the test still runs — it goes red
because nothing is there. The TEST must exist.

The test receives the implementation as an ARGUMENT (`lua5.4 t.lua impl`),
except for pytest suites (`test_*.py`, `*_test.py`) which run under `pytest -q`
and find it in the environment variable BULLPEN_IMPL.
"""
import argparse
import hashlib
import json
import os
import socket
import subprocess
import sys
import time


def checksum(path):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for block in iter(lambda: f.read(65536), b""):
            h.update(block)
    return h.hexdigest()


def commit_of(path):
    """Commit of the tree the file lives in, or None. Never fatal: a subject
    outside a repository is a normal case, not an error."""
    d = os.path.dirname(os.path.abspath(path)) or "."
    try:
        p = subprocess.run(["git", "-C", d, "rev-parse", "HEAD"],
                           capture_output=True, text=True, timeout=10)
        if p.returncode == 0:
            short = (p.stdout or "").strip()[:12]
            q = subprocess.run(["git", "-C", d, "status", "--porcelain"],
                               capture_output=True, text=True, timeout=10)
            dirty = bool((q.stdout or "").strip())
            return short + ("+dirty" if dirty else "")
    except Exception:
        pass
    return None


def runner(test, impl):
    """Command and environment for this test. By file kind, deliberately short:
    another language costs two lines here, not a heuristic.

    The pytest line is the rule, not an exception — @testdesigner's role file
    asks for a pytest suite. Started as a script (`python3 tests/test_x.py`)
    such a file runs NOTHING and exits 0, so the tool would call every pytest
    file green. Hence the name convention check.

    pytest gets the subject through the environment, not as an argument: a
    second argument would be read as another TEST PATH and abort the run."""
    env = dict(os.environ)
    name = os.path.basename(test)
    if test.endswith(".lua"):
        return ["lua5.4", test, impl], env
    if name.startswith("test_") or name.endswith("_test.py"):
        env["BULLPEN_IMPL"] = os.path.abspath(impl)
        return [sys.executable, "-m", "pytest", "-q", test], env
    if test.endswith(".py"):
        return [sys.executable, test, impl], env
    if os.access(test, os.X_OK):
        return [test, impl], env
    raise SystemExit(f"bullpen-attest: no runner known for {test}")


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--impl", required=True, help="the subject under test")
    ap.add_argument("--test", required=True, help="the separate acceptance test")
    ap.add_argument("--timeout", type=int, default=300)
    ap.add_argument("--json", action="store_true")
    a = ap.parse_args()

    # The test must exist — without a specification there is nothing to attest.
    if not os.path.isfile(a.test):
        raise SystemExit(f"bullpen-attest: {a.test} does not exist")

    # The subject may be missing. That is the STARTING STATE of a campaign, and
    # attesting it is a step of its own: "with nothing here, the specification
    # is red". Aborting would force the tester to back the most important red
    # run with prose — exactly what this certificate exists to abolish.
    impl_missing = not os.path.isfile(a.impl)

    cmd, env = runner(a.test, a.impl)
    started = time.time()
    try:
        p = subprocess.run(cmd, capture_output=True, text=True,
                           timeout=a.timeout, env=env)
        rc, out = p.returncode, ((p.stdout or "") + (p.stderr or ""))
    except subprocess.TimeoutExpired:
        rc, out = 124, f"(timeout of {a.timeout}s exceeded)"
    duration = round(time.time() - started, 1)

    # A green run against a MISSING implementation is a contradiction, and it is
    # the most dangerous certificate this tool could issue: it would prove that
    # the specification does not test the implementation at all. Measured on
    # 2026-08-08 with a throwaway test that ignored its argument — verdict
    # GRUEN, subject absent. The return code is still reported as measured (the
    # tool does not judge), but the certificate refuses to call itself an
    # acceptance, and the exit code says so.
    contradiction = impl_missing and rc == 0
    verdict = "UNBRAUCHBAR" if contradiction else ("GRUEN" if rc == 0 else "ROT")

    cert = {
        "pruefling": {"pfad": a.impl,
                      "sha256": None if impl_missing else checksum(a.impl),
                      "commit": None if impl_missing else commit_of(a.impl),
                      "fehlt": impl_missing},
        "test":      {"pfad": a.test, "sha256": checksum(a.test),
                      "commit": commit_of(a.test)},
        "befehl": " ".join(cmd),
        "impl_via": "BULLPEN_IMPL" if "-m" in cmd and "pytest" in cmd else "argv",
        "rueckgabe": rc,
        "urteil": verdict,
        "widerspruch": contradiction,
        "container": socket.gethostname(),
        "ts": int(started),
        "dauer_s": duration,
        "ausgabe_ende": [z for z in out.strip().splitlines()[-6:]],
    }

    if a.json:
        print(json.dumps(cert, ensure_ascii=False))
        return 3 if contradiction else rc

    def addr(d):
        if d.get("fehlt"):
            return "— the file does not exist (starting state)"
        return d["sha256"][:16] + (f"  commit {d['commit']}" if d["commit"] else "")

    print("ABNAHME")
    print(f"  urteil     : {verdict}   (rueckgabe {rc})")
    print(f"  pruefling  : {a.impl}")
    print(f"               sha256 {addr(cert['pruefling'])}")
    print(f"  test       : {a.test}")
    print(f"               sha256 {addr(cert['test'])}")
    print(f"  befehl     : {cert['befehl']}")
    if cert["impl_via"] == "BULLPEN_IMPL":
        print(f"  pruefling ueber: BULLPEN_IMPL={os.path.abspath(a.impl)}")
    print(f"  ausgefuehrt: {cert['container']}  in {duration}s")
    print("  ausgabe    :")
    for z in cert["ausgabe_ende"]:
        print(f"    {z[:110]}")
    print()
    if contradiction:
        print("  WIDERSPRUCH: the implementation is ABSENT and the test passed anyway.")
        print("  This is not an acceptance. A specification that goes green without")
        print("  a subject does not test the subject — fix the test, not the code.")
        print("  Exit code 3, so no && chain mistakes this for a pass.")
    else:
        print("  Nachrechnen: same two checksums, same command, same number.")
        print("  If something differs, the test is not at fault — the claim is.")
    return 3 if contradiction else rc


sys.exit(main())
