"""CAIN-A75-025 offline verifier (rules embedded verbatim). CAIN-A75-025 fleet-governance-design rules. Standard library only, no CAIN imports: embedded verbatim in the offline
verifier.

A design claims it can govern a large concurrent fleet. The review recomputes the design against its own numbers:

  capacity    planned agents x per-agent request rate fits the control plane at the declared headroom, in every region
  identity    a root of trust and workload identity are named, credentials rotate within the limit, and every agent gets
              its own credential (no shared fleet secret)
  policy      rollouts are canary-first with a small canary and a fast rollback; the policy revision is hashed
  containment a kill switch exists above per-agent scope, was tested, and halts within the limit
  isolation   tenant namespaces, cross-tenant denial and noisy-neighbour limits are all present
  inventory   a source of truth, frequent reconciliation and full coverage are declared
  claims      every boolean the design asserts matches what this review computed

Verdict: SOUND, else UNSOUND with every failing check named. Malformed input is UNSOUND.
It reviews a design record; it does not provision or load-test a fleet.
"""
from __future__ import annotations

import math
import re

_ID = re.compile(r"^[A-Za-z0-9_.:\-]{1,80}$")
_HEX = re.compile(r"^[0-9a-f]{64}$")


class Bad(ValueError):
    pass


def _int(v, what, lo=0, hi=10 ** 12):
    if isinstance(v, bool) or not isinstance(v, int) or not (lo <= v <= hi):
        raise Bad(f"{what} must be an integer in [{lo}, {hi}]")
    return v


def _num(v, what, lo=0.0, hi=1e12):
    if isinstance(v, bool) or not isinstance(v, (int, float)) or not math.isfinite(v) or not (lo <= v <= hi):
        raise Bad(f"{what} must be a finite number in [{lo}, {hi}]")
    return float(v)


def evaluate(record):
    checks, summary = [], {}

    def c(cid, ok, detail=""):
        checks.append({"id": cid, "ok": bool(ok), "detail": detail})

    try:
        if not isinstance(record, dict) or record.get("schema") != "cain42.a75.fleet.design.v1":
            raise Bad("not a cain42.a75.fleet.design.v1 record")
        fleet, cap, ident = record.get("fleet"), record.get("capacity"), record.get("identity")
        pol, cont, iso = record.get("policy"), record.get("containment"), record.get("isolation")
        inv = record.get("inventory")
        if not all(isinstance(x, dict) for x in (fleet, cap, ident, pol, cont, iso, inv)):
            raise Bad("fleet, capacity, identity, policy, containment, isolation and inventory must be objects")

        agents = _int(fleet.get("planned_agents"), "fleet.planned_agents", 1)
        regions = _int(fleet.get("regions"), "fleet.regions", 1, 1000)
        tenants = _int(fleet.get("tenants"), "fleet.tenants", 1)
        per_agent = _num(cap.get("per_agent_qps"), "capacity.per_agent_qps", 0.0)
        replicas = _int(cap.get("control_plane_replicas"), "capacity.control_plane_replicas", 1, 100000)
        cp_qps = _num(cap.get("control_plane_qps_per_replica"), "capacity.control_plane_qps_per_replica", 0.0)
        headroom = _num(cap.get("headroom_pct"), "capacity.headroom_pct", 0.0, 99.0)
        demand = agents * per_agent
        supply = replicas * cp_qps * (1.0 - headroom / 100.0)
        c("capacity.total", demand <= supply + 1e-9, f"demand {round(demand, 3)} qps <= usable supply {round(supply, 3)} qps")
        per_region = demand / regions
        region_supply = supply / regions
        c("capacity.per_region", per_region <= region_supply + 1e-9,
          f"per region {round(per_region, 3)} <= {round(region_supply, 3)}")
        c("capacity.headroom", headroom >= 20.0, f"headroom {headroom}% >= 20%")
        summary["demand_qps"] = round(demand, 3)
        summary["usable_supply_qps"] = round(supply, 3)

        c("identity.root", isinstance(ident.get("root_of_trust"), str) and bool(ident["root_of_trust"]), "a root of trust is named")
        c("identity.workload", isinstance(ident.get("workload_identity"), str) and bool(ident["workload_identity"]), "a workload identity system is named")
        rot = _num(ident.get("rotation_days"), "identity.rotation_days", 0.0, 36500.0)
        c("identity.rotation", rot <= 90.0, f"rotation {rot} days <= 90")
        c("identity.per_agent", ident.get("per_agent_credential") is True, "every agent has its own credential")

        c("policy.canary_first", pol.get("rollout") == "canary", f"rollout {pol.get('rollout')!r}")
        pct = _num(pol.get("canary_pct"), "policy.canary_pct", 0.0, 100.0)
        c("policy.canary_small", 0.0 < pct <= 10.0, f"canary {pct}% in (0, 10]")
        rollback = _num(pol.get("rollback_seconds"), "policy.rollback_seconds", 0.0)
        c("policy.fast_rollback", rollback <= 300.0, f"rollback {rollback}s <= 300s")
        c("policy.revision_hash", isinstance(pol.get("policy_revision_sha256"), str) and bool(_HEX.match(pol["policy_revision_sha256"])),
          "the policy revision is hashed")

        c("containment.scope", cont.get("kill_switch_scope") in ("global", "region", "tenant"), f"scope {cont.get('kill_switch_scope')!r}")
        c("containment.tested", cont.get("tested") is True, "the kill switch was tested")
        halt = _num(cont.get("max_halt_seconds"), "containment.max_halt_seconds", 0.0)
        c("containment.fast_halt", halt <= 60.0, f"halt {halt}s <= 60s")

        c("isolation.tenant_namespace", iso.get("tenant_namespace") is True, "tenant namespaces")
        c("isolation.cross_tenant_denied", iso.get("cross_tenant_denied") is True, "cross-tenant traffic is denied")
        c("isolation.noisy_neighbor", iso.get("noisy_neighbor_limits") is True, "noisy-neighbour limits")

        c("inventory.source_of_truth", isinstance(inv.get("source_of_truth"), str) and bool(inv["source_of_truth"]), "an inventory source is named")
        reconcile = _num(inv.get("reconciliation_hours"), "inventory.reconciliation_hours", 0.0, 100000.0)
        c("inventory.reconciled", reconcile <= 24.0, f"reconciles every {reconcile}h <= 24h")
        coverage = _num(inv.get("coverage_pct"), "inventory.coverage_pct", 0.0, 100.0)
        c("inventory.coverage", coverage >= 100.0, f"coverage {coverage}%")

        claims = record.get("claims") if isinstance(record.get("claims"), dict) else {}
        computed = {
            "capacity_sufficient": demand <= supply + 1e-9,
            "identity_per_agent": ident.get("per_agent_credential") is True and rot <= 90.0,
            "containment_tested": cont.get("tested") is True and halt <= 60.0,
        }
        for key in sorted(claims):
            c(f"claims.{key}", key in computed and claims[key] == computed[key], f"claimed {claims[key]!r}, computed {computed.get(key)!r}")
        summary["computed"] = computed
        summary["agents"] = agents
        summary["tenants"] = tenants
    except Bad as e:
        checks.append({"id": "input.well_formed", "ok": False, "detail": str(e)})
        return "UNSOUND", checks, summary
    except Exception as e:  # noqa: BLE001
        checks.append({"id": "input.error", "ok": False, "detail": f"{type(e).__name__}: {e}"[:200]})
        return "UNSOUND", checks, summary
    return ("SOUND" if all(x["ok"] for x in checks) else "UNSOUND"), checks, summary


# ---------------------------------------------------------------------------- offline verifier (no CAIN imports)
# Every name below is prefixed _kit_verifier_ / _b64 / _hl / _json / _sys so it cannot shadow a name in the rules above.
import base64 as _b64, hashlib as _hl, json as _json, sys as _sys
_KIT_VERIFIER_GENESIS = _hl.sha256(b"cain42.a75.fleet.genesis").hexdigest()


def _kit_verifier_canon(o):
    return _json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()


def _kit_verifier_sha(o):
    return _hl.sha256(_kit_verifier_canon(o)).hexdigest()


def _kit_verifier_main(argv):
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
    if len(argv) < 2:
        print("usage: verify_offline.py run.json --pinned-key BASE64"); return 2
    run = _json.load(open(argv[1]))
    pin = argv[argv.index("--pinned-key") + 1] if "--pinned-key" in argv else None
    problems, prev, recs = [], _KIT_VERIFIER_GENESIS, run.get("records") or []
    for i, r in enumerate(recs):
        body = {k: v for k, v in r.items() if k not in ("record_hash", "signature_b64")}
        if _kit_verifier_sha(body) != r.get("record_hash"): problems.append(f"record {i}: record_hash does not match")
        if body.get("subject_sha256") != _kit_verifier_sha(body.get("subject")): problems.append(f"record {i}: subject_sha256 does not match")
        if pin and r.get("public_key_b64") != pin: problems.append(f"record {i}: different key than the pinned one")
        try:
            Ed25519PublicKey.from_public_bytes(_b64.b64decode(pin or r.get("public_key_b64"))).verify(
                _b64.b64decode(r.get("signature_b64", "")), str(r.get("record_hash")).encode())
        except Exception:
            problems.append(f"record {i}: signature does not verify")
        if r.get("prev_record_hash") != prev: problems.append(f"record {i}: chain link broken")
        v, checks, _ = evaluate(r.get("subject"))
        if v != r.get("verdict") or checks != r.get("checks"):
            problems.append(f"record {i}: re-running the rules gives {v}, the record says {r.get('verdict')}")
        prev = r.get("record_hash")
    if run.get("run_hash") != _kit_verifier_sha([r.get("record_hash") for r in recs]): problems.append("run_hash does not match")
    for p in problems: print("  -", p)
    if not pin: print("WARNING: no --pinned-key given; the signature was checked against the key inside the file")
    print("VERDICT:", "VERIFIED" if not problems else "TAMPERED OR INVALID", f"(CAIN-A75-025, {len(recs)} record(s), verdicts {run.get('verdicts')})")
    return 0 if not problems else 1


if __name__ == "__main__":
    _sys.exit(_kit_verifier_main(_sys.argv))
