#!/usr/bin/env python3
"""CAIN-42 multi-source independent verification (Prompt 2 Part 41; "more than one source").

Runs every available INDEPENDENT verifier (each imports no CAIN code, each written separately) over
the published evidence and this driver's own fresh verification of the signed claims registry, then
aggregates their verdicts into one report.

Honest scope, stated in the output and never blurred:
  * "independent" here means INDEPENDENT IMPLEMENTATIONS BY DIFFERENT SESSIONS THAT SHARE NO CODE,
    plus this driver's own separate implementation. It does NOT mean a third party: every verifier
    is operated by the same project. `independent_third_party` is always false.

    python3 scripts/cain42_l5/verify_multisource.py [--out CAIN42_MULTISOURCE_VERIFICATION.json] [--json]

Exit 0 only if every source verifies (each source's own verdict); INCOMPLETE/INVALID is not success.
"""
from __future__ import annotations

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

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

ROOT = Path(__file__).resolve().parents[2]
SITES = {"cainstudio.online": "https://cainstudio.online/proof/bundle/",
         "mcpgate.online": "https://mcpgate.online/proof/bundle/",
         "clawx.click": "https://clawx.click/evidence/"}
REGISTRY_REL = "claims/CAIN42_FINAL_PUBLIC_CLAIMS.json"


# --------------------------------------------------------------------------- this driver's own source
def canon(o) -> bytes:
    return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()


def sha256_hex(b: bytes) -> str:
    return hashlib.sha256(b).hexdigest()


def fetch(url: str) -> bytes:
    req = urllib.request.Request(url, headers={"User-Agent": "cain-multisource/1"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return r.read()


def own_registry_check() -> dict:
    """This driver's OWN verification (no CAIN imports, no reuse of the other tools): fetch the signed
    claims registry from all three sites, prove they are byte-identical, and verify the Ed25519
    signature over registry_digest with an independently constructed message."""
    bodies = {}
    for site, base in SITES.items():
        bodies[site] = fetch(base + REGISTRY_REL)
    hashes = {s: sha256_hex(b) for s, b in bodies.items()}
    identical = len(set(hashes.values())) == 1
    reg = json.loads(next(iter(bodies.values())))
    pub = reg.get("evidence_root_public_key_b64", "")
    sig = reg.get("signature_b64", "")
    digest = reg.get("registry_digest", "")
    sig_ok = False
    try:
        Ed25519PublicKey.from_public_bytes(base64.b64decode(pub)).verify(base64.b64decode(sig), digest.encode())
        sig_ok = True
    except (InvalidSignature, ValueError, TypeError):
        sig_ok = False
    claims = reg.get("claims", [])
    from collections import Counter
    return {"status": "VALID" if (identical and sig_ok) else "INVALID",
            "registry_identical_on_all_sites": identical, "registry_hashes": hashes,
            "signature_verified": sig_ok, "registry_digest": digest, "claims": len(claims),
            "claims_by_status": dict(Counter(c.get("status") for c in claims)),
            "key_epoch": reg.get("key_epoch"), "issued_at": reg.get("issued_at")}


# --------------------------------------------------------------------------- external sources
def run(cmd: list) -> tuple:
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=900, cwd=str(ROOT))
        return r.returncode, (r.stdout + r.stderr)
    except Exception as exc:
        return None, str(exc)


def source_verify_all() -> dict:
    rc, out = run([sys.executable, "scripts/cain42_site/verify_all.py"])
    intact = "INTACT" in out
    return {"name": "verify_all.py", "covers": "every claim + artifact, from all 3 sites",
            "verdict": "VALID" if (rc == 0 and intact) else "INVALID", "detail": out.strip().splitlines()[-1] if out.strip() else ""}


def source_authority(path: str, label: str, bundle: str) -> dict:
    rc, out = run([sys.executable, path, bundle])
    v = "INVALID"
    if '"verdict"' in out:
        try:
            v = json.loads(out[out.index("{"):]).get("verdict", "INVALID")
        except Exception:
            v = "VALID" if '"verdict": "VALID"' in out else "INVALID"
    elif "authority bundle: VALID" in out:
        v = "VALID"
    return {"name": label, "covers": "the L5 authority bundle (AgentIdentity/AuthorityGrant/DelegationGrant/Decision)",
            "verdict": v, "detail": (out.strip().splitlines() or [""])[0][:200]}


def source_l5_bundle() -> dict | None:
    p = ROOT / "scripts" / "cain42_l5" / "verify_l5_bundle.py"
    b = ROOT / "CAIN42_L5_EVIDENCE_BUNDLE.json"
    if not (p.exists() and b.exists()):
        return None
    rc, out = run([sys.executable, str(p), str(b)])
    v = "INVALID"
    try:
        v = json.loads(out[out.index("{"):]).get("verdict", "INVALID")
    except Exception:
        v = "VALID" if "\"verdict\": \"VALID\"" in out else "INVALID"
    return {"name": "verify_l5_bundle.py", "covers": "the L5 evidence bundle (authority root, autonomous autonomy)",
            "verdict": v, "detail": (out.strip().splitlines() or [""])[0][:120]}


def main() -> int:
    out_path = ROOT / "CAIN42_MULTISOURCE_VERIFICATION.json"
    if "--out" in sys.argv:
        out_path = Path(sys.argv[sys.argv.index("--out") + 1])

    bundle = "CAIN42_L5_AUTHORITY_BUNDLE.json"
    sources = [
        source_verify_all(),
        source_authority("scripts/cain42_l5/verify_authority_bundle.py",
                         "verify_authority_bundle.py (session A)", bundle),
        source_authority("scripts/cain42_l5/verify_authority_bundle_engine_b.py",
                         "verify_authority_bundle_engine_b.py (session B)", bundle),
    ]
    l5 = source_l5_bundle()
    if l5:
        sources.append(l5)
    own = own_registry_check()
    sources.append({"name": "verify_multisource.py (this driver, own implementation)",
                    "covers": "signed claims registry: cross-site identity + Ed25519 over registry_digest",
                    "verdict": own["status"], "detail": f"{own['claims']} claims, key_epoch {own['key_epoch']}"})

    all_valid = all(s["verdict"] == "VALID" for s in sources)
    report = {
        "schema": "cain42.multisource-verification.v1",
        "generated_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
        "claim": ("As of 2026-09-28, the published CAIN-42 evidence is verified by more than one "
                  "independent verifier implementation that shares no code (separately written clean-room "
                  "verifiers plus this driver's own implementation), and they agree."),
        "scope": ("Multi-implementation / multi-source verification. Every verifier is operated by the same "
                  "project; NO third party has reviewed CAIN-42. This is not a certification."),
        "independent_third_party": False,
        "sources_count": len(sources),
        "all_sources_valid": all_valid,
        "overall": "VERIFIED_MULTI_SOURCE" if all_valid else "INCOMPLETE",
        "sources": sources,
        "own_registry_check": own,
    }
    out_path.write_text(json.dumps(report, indent=1) + "\n")
    if "--json" in sys.argv:
        print(json.dumps(report, indent=1))
    else:
        print(f"multi-source verification: {report['overall']}  ({len(sources)} independent implementations)")
        for s in sources:
            print(f"  {s['verdict']:8s} {s['name']:52s} {s['covers']}")
        print(f"  third party: {report['independent_third_party']}  (same operator; not a certification)")
    return 0 if all_valid else 2


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