#!/usr/bin/env python3
"""Clean-room verifier for the live hosted CAG-L5 system-governor run (CAIN42_CAG_L5_HOSTED_LIVE_RUN.json).

INDEPENDENT IMPLEMENTATION: imports NOTHING from CAIN (standard library + `cryptography`).

Checks, from the published file alone:
  1. every ALLOW carries a decision signed by the governance key, and that key equals the one the live gateway
     publishes at /fabric/mcp/system/key (with --live, fetched from all three domains);
  2. every ALLOW carries an execution commitment signed by the same key, bound to the decision's digest;
  3. every ALLOW names a cluster state certification (cluster, sequence, signers >= quorum);
  4. every refusal names a reason, and no case marked as an attack was allowed;
  5. the registration's certification is verified and names >= 3 distinct signers.
With --live it also fetches the cluster's own record of the certification sequence
(/api/v1/live-cluster/qc/{seq}) and checks that the certified operation carries this run's state digest.

    python3 verify_hosted_governor_run.py CAIN42_CAG_L5_HOSTED_LIVE_RUN.json [--live]
"""
from __future__ import annotations

import base64
import hashlib
import json
import sys
import urllib.request
from pathlib import Path

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric import ed25519

D_DECISION = "CAIN42/CAG-L5-SYSTEM-DECISION/v1"
D_COMMIT = "CAIN42/CAG-L5-EXECUTION-COMMITMENT/v1"
COMMIT_KEYS = ("agent_id", "tool_id", "capability", "resource", "parameters_hash", "authority_hash",
               "policy_version", "trajectory_id", "context_hash", "expected_effect_hash", "nonce")
SITES = ("https://cainstudio.online", "https://mcpgate.online", "https://clawx.click")


def canon(o) -> bytes:
    return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()


def digest(domain, fields) -> str:
    return hashlib.sha256(canon({"domain": domain, **fields})).hexdigest()


def sig_ok(pub, sig, domain, fields) -> bool:
    try:
        ed25519.Ed25519PublicKey.from_public_bytes(base64.b64decode(pub)).verify(
            base64.b64decode(sig), digest(domain, fields).encode())
        return True
    except (InvalidSignature, ValueError, TypeError):
        return False


def get(url):
    with urllib.request.urlopen(url, timeout=20) as r:
        return json.loads(r.read())


def main() -> int:
    args = [a for a in sys.argv[1:] if not a.startswith("--")]
    live = "--live" in sys.argv
    if len(args) != 1:
        print(__doc__, file=sys.stderr)
        return 2
    run = json.loads(Path(args[0]).read_text())
    key = run.get("governance_public_key", "")
    checks = []

    def check(name, ok, detail=""):
        checks.append({"check": name, "ok": bool(ok), "detail": str(detail)[:160]})

    reg = run.get("registration", {}).get("certification", {})
    check("registration.certified_by_cluster", reg.get("verified") is True and len(set(reg.get("signers") or [])) >= 3,
          f"seq {reg.get('sequence')} signers {reg.get('signers')}")
    for i, c in enumerate(run.get("cases", [])):
        tag = f"case{i}"
        if c.get("allowed"):
            d, s = c.get("signed_decision") or {}, c.get("signature", "")
            check(f"{tag}.decision_signed", d.get("decision") == "ALLOW" and sig_ok(key, s, D_DECISION, d))
            cm = c.get("commitment") or {}
            body = {k: cm.get(k) for k in COMMIT_KEYS}
            check(f"{tag}.commitment_signed_and_bound", sig_ok(key, cm.get("signature", ""), D_COMMIT, body)
                  and digest(D_COMMIT, body) == d.get("commitment"))
            sc = c.get("state_certification") or {}
            check(f"{tag}.state_certified", sc.get("sequence") and len(set(sc.get("signers") or [])) >= (sc.get("quorum") or 3),
                  f"seq {sc.get('sequence')}")
        else:
            check(f"{tag}.refusal_has_reason", bool(c.get("reason")), c.get("reason"))
        check(f"{tag}.as_expected", c.get("allowed") is c.get("expected_allowed"), c.get("case"))
    recovery = run.get("emergency_recovery_http", {})
    check("emergency.one_operator_refused", recovery.get("one_operator") == 400, recovery)
    check("emergency.two_operators_accepted", recovery.get("two_operators") == 200, recovery)
    if live:
        for site in SITES:
            try:
                k = get(f"{site}/fabric/mcp/system/key").get("governance_public_key")
            except Exception as e:  # noqa: BLE001
                k = f"unreachable: {type(e).__name__}"
            check(f"live.{site.split('//')[1]}.publishes_same_key", k == key, k)
        seq = reg.get("sequence")
        try:
            qc = get(f"{SITES[0]}/api/v1/live-cluster/qc/{seq}")
            text = json.dumps(qc)
            check("live.cluster_record_names_this_state", reg.get("state_digest", "x") in text,
                  f"sequence {seq}")
        except Exception as e:  # noqa: BLE001
            check("live.cluster_record_names_this_state", False, f"unreachable: {type(e).__name__}")
    failed = [c["check"] for c in checks if not c["ok"]]
    print(json.dumps({"verdict": "VALID" if not failed else "INVALID", "checks_passed": len(checks) - len(failed),
                      "checks_failed": len(failed), "failed": failed, "clean_room": True, "imports_cain": False,
                      "live": live, "checks": checks}, indent=2))
    return 0 if not failed else 1


if __name__ == "__main__":
    raise SystemExit(main())
