#!/usr/bin/env python3
"""Verify a PROOFLOOP evaluation record offline. Standard library + `cryptography`; imports no CAIN code.

    pip install cryptography
    curl -fsSLO https://cainstudio.online/verify_proofloop_eval.py
    python3 -I verify_proofloop_eval.py fresh_evidence.json            # human report
    python3 -I verify_proofloop_eval.py fresh_evidence.json --json

Checks, all on your machine:
  1. KEY      the pinned public key has the published fingerprint (or pass --public-key / --fingerprint)
  2. SIG      the Ed25519 signature is valid for the canonical record (sorted keys, no whitespace)
  3. HASH     record_sha256 is the SHA-256 of that canonical record
  4. COUNTS   the headline counts are recomputed from the per-finding table (not trusted from `summary`)
The record names the sha256 of its two inputs (the blind labels and the AI run output); to go further, obtain
those files and confirm their sha256 matches `inputs`. Exit code 0 only when checks 1-4 pass.
"""
from __future__ import annotations

import argparse
import base64
import hashlib
import json
import sys

PUBLIC_KEY_B64 = "b6hfA2rpAdV29FOdhNXGkk4t91mCV0AnfFYwq0HO0R8="
FINGERPRINT = "SHA256:2232b4add9b35d4016e5be270df4ed56e0dc9b39a0530ac69891073253aae90d"


def recompute(table):
    c = {"real_kept": 0, "real_demoted": 0, "plausible_kept": 0, "plausible_demoted": 0,
         "false_kept": 0, "false_demoted": 0, "no_ai_row": 0}
    cls = {"REAL": "real", "PLAUSIBLE": "plausible", "FALSE_ALARM": "false"}
    for r in table:
        if not r.get("ai_present"):
            c["no_ai_row"] += 1
            continue
        c[f"{cls[r['human_label']]}_{'demoted' if r.get('ai_demoted') else 'kept'}"] += 1
    return c


def main(argv=None) -> int:
    ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    ap.add_argument("record")
    ap.add_argument("--public-key", default=PUBLIC_KEY_B64)
    ap.add_argument("--fingerprint", default=FINGERPRINT)
    ap.add_argument("--json", action="store_true")
    a = ap.parse_args(argv)
    checks = []

    def add(name, ok, detail=""):
        checks.append({"check": name, "ok": bool(ok), "detail": detail})
        return ok

    try:
        from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
    except ImportError:
        print("needs: pip install cryptography", file=sys.stderr)
        return 2
    rec = json.load(open(a.record))
    raw = base64.b64decode(a.public_key)
    fp = "SHA256:" + hashlib.sha256(raw).hexdigest()
    add("KEY", fp.lower() == a.fingerprint.strip().lower(), fp)
    sig = rec.get("signature", {})
    body = {k: rec[k] for k in rec if k not in ("record_sha256", "signature", "signed_payload_b64")}
    payload = json.dumps(body, sort_keys=True, separators=(",", ":")).encode()
    try:
        Ed25519PublicKey.from_public_bytes(raw).verify(base64.b64decode(sig["sig_b64"]), payload)
        add("SIG", True, sig.get("key_fingerprint", ""))
    except Exception as e:  # noqa: BLE001
        add("SIG", False, type(e).__name__)
    add("HASH", rec.get("record_sha256") == hashlib.sha256(payload).hexdigest(), rec.get("record_sha256", "")[:16])
    if rec.get("signed_payload_b64"):
        add("BYTES", base64.b64decode(rec["signed_payload_b64"]) == payload, "exact signed bytes match the record")
    want = recompute(rec.get("table", []))
    add("COUNTS", want == rec.get("summary", {}).get("counts"), json.dumps(want))
    ok = all(c["ok"] for c in checks)
    s = rec.get("summary", {})
    if a.json:
        print(json.dumps({"ok": ok, "checks": checks, "summary": s}, indent=2))
    else:
        for c in checks:
            print(f"  {'PASS' if c['ok'] else 'FAIL'}  {c['check']:7s} {c['detail']}")
        print(f"\nFRESH split: {s.get('big_findings_labelled')} BIG findings; "
              f"{s.get('real_bugs')} real, {s.get('plausible')} plausible, {s.get('false_alarms')} false alarms.")
        print(f"AI removed {s.get('ai_false_alarms_removed')} false alarms; "
              f"wrongly demoted {s.get('ai_real_bugs_wrongly_demoted')}.")
        print(f"Precision rules-only {s.get('precision_rules_only')} -> after AI {s.get('precision_after_ai')}; "
              f"real-bug recall after AI {s.get('recall_real_kept_after_ai')}.")
        print("VERIFIED" if ok else "NOT VERIFIED")
    return 0 if ok else 1


if __name__ == "__main__":
    sys.exit(main())
