#!/usr/bin/env python3
"""Clean-room verifier for CAIN42_TRUST_INTEGRITY_LIVE_RUN.json. Standard library only; no CAIN imports.

Offline it checks the run file itself: every case the 2026-09-28 trust-integrity fix is about has
the outcome the fix promises, on all three domains.

With --live it also checks every recorded decision against the PBFT cluster's OWN public record,
GET /api/v1/live-cluster/qc/{sequence}?cluster=cain-mr-01, which anyone can fetch:
  - the cluster ordered that exact decision id at that sequence,
  - the verdict the cluster was asked to sign (pre_verdict) is the verdict in the run file,
  - the commitment hash matches, and a commit certificate carries >= 3 distinct replica signers.
So "the attacker's $250,000 transfer was not ALLOWED" is checkable from the cluster, not from us.

Usage: python3 verify_trust_integrity_run.py CAIN42_TRUST_INTEGRITY_LIVE_RUN.json [--live]
Exit 0 = VALID.
"""
from __future__ import annotations

import json
import sys
import urllib.request

QC_SITE = "https://clawx.click"


def get(url):
    req = urllib.request.Request(url, headers={"accept": "application/json", "user-agent": "trust-integrity-verifier/1"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)


def verify(doc, live):
    errors, checked = [], 0

    def check(ok, msg):
        if not ok:
            errors.append(msg)

    check(doc.get("schema") == "CAIN42/TRUST-INTEGRITY-LIVE-RUN/v1", "schema")
    domains = doc.get("domains") or []
    check(len(domains) == 3, "three domains")
    for d in domains:
        name = d.get("domain")
        cs = d.get("cases") or []
        by = lambda c: [x for x in cs if x.get("case") == c]
        t1 = by("T1")
        check(t1 and t1[0].get("verdict") == "REQUIRE_APPROVAL" and (t1[0].get("approval") or {}).get("approval_id"),
              f"{name} T1: new agent held and queued")
        check(len(by("T2")) == 2 and all(x.get("verdict") == "BLOCKED" for x in by("T2")), f"{name} T2: injections blocked")
        t3 = by("T3")
        check(len(t3) == 3, f"{name} T3: three dangerous actions")
        for x in t3:
            check(not str(x.get("verdict")).startswith("ALLOWED"), f"{name} T3 {x.get('path')}: must not be ALLOWED")
            lvl = ((x.get("trust") or {}).get("action_risk") or {}).get("level")
            check(lvl in ("high", "critical"), f"{name} T4 {x.get('path')}: action risk {lvl}")
        t5 = by("T5")
        check(len(t5) == 5 and all(x.get("verdict") == "BLOCKED" for x in t5), f"{name} T5: every spelling blocked")
        t6 = by("T6")
        check(t6 and (t6[0].get("trust") or {}).get("agent_trust_state") == "unknown"
              and not str(t6[0].get("verdict")).startswith("ALLOWED"), f"{name} T6: new label capped")
        t7 = by("T7")
        check(t7 and t7[0].get("enforcing") is True and t7[0].get("mode") == "enforce" and t7[0].get("chain_has_ran"),
              f"{name} T7: demo states enforcement")

        t8 = [x for x in by("T8") if x.get("path")]
        ap = by("T8-approvals")
        check(len(t8) == 4 and ap, f"{name} T8: solo-developer path recorded")
        if len(t8) == 4 and ap:
            check(t8[0].get("verdict") == "REQUIRE_APPROVAL", f"{name} T8: agent held first")
            check(ap[0].get("self_approval_http") == 403 and ap[0].get("owner_approval_http") == 200,
                  f"{name} T8: agent cannot approve itself; owner can")
            check(str(t8[1].get("verdict")).startswith("ALLOWED"), f"{name} T8: approved retry runs")
            check(str(t8[2].get("verdict")).startswith("ALLOWED")
                  and (t8[2].get("trust") or {}).get("trust_state") == "normal", f"{name} T8: earned autonomy")
            check(not str(t8[3].get("verdict")).startswith("ALLOWED"), f"{name} T8: critical still held")

        if live:
            for x in cs:
                c = x.get("consensus") or {}
                if not x.get("decision_id") or c.get("sequence") is None:
                    continue
                try:
                    rec = get(f"{QC_SITE}/api/v1/live-cluster/qc/{c['sequence']}?cluster={c.get('cluster_id') or 'cain-mr-01'}")
                except Exception as e:                                  # noqa: BLE001
                    errors.append(f"{name} {x['decision_id']}: cluster record unreachable ({e})")
                    continue
                data = (((rec.get("prepare_qc") or {}).get("leader_proposal") or {}).get("payload") or {}) \
                    .get("operation", {}).get("data", {})
                commit = rec.get("commit_qc") or {}
                check(data.get("decision_id") == x["decision_id"], f"{name} {x['decision_id']}: cluster ordered this decision")
                check(data.get("pre_verdict") == x.get("verdict") or data.get("pre_verdict") == c.get("pre_verdict"),
                      f"{name} {x['decision_id']}: cluster pre_verdict {data.get('pre_verdict')} vs run {x.get('verdict')}")
                check(data.get("commitment_sha256") == c.get("commitment_sha256"), f"{name} {x['decision_id']}: commitment")
                check(len(set(commit.get("signer_set") or [])) >= 3 and commit.get("quorum", 0) >= 3,
                      f"{name} {x['decision_id']}: commit certificate has a 3-of-4 quorum")
                checked += 1
    return errors, checked


def main():
    if len(sys.argv) < 2:
        print(__doc__)
        return 2
    doc = json.load(open(sys.argv[1]))
    errors, checked = verify(doc, "--live" in sys.argv)
    for e in errors:
        print("FAIL", e)
    if "--live" in sys.argv:
        print(f"decisions checked against the cluster's own record: {checked}")
    print("VALID" if not errors else "INVALID")
    return 0 if not errors else 1


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