#!/usr/bin/env python3
"""ABOM inline gate — the 2-minute sales demo.

Story: a loan agent is prompt-injected and tries to wire $1,000,000 out via a
tool that is NOT in its signed manifest. ABOM blocks the call *before it runs*,
notarizes the denial in a tamper-evident transparency log, and an independent
auditor verifies the proof without trusting the operator.

    python demo/gate_demo.py
"""
from __future__ import annotations

from abom.gate import Action, ActionDenied, Gate, Verdict
from abom.signer import verify_payload
from abom.audit import canonical_json
from abom.merkle import verify_inclusion_hex


def line(c="─"):
    print(c * 64)


SIGNED_MANIFEST = {
    "abom": "0.1",
    "type": "CompositionManifest",
    "agent": {"name": "loan-doc-agent", "version": "1.4.0", "risk_class": "high (Annex III)"},
    "components": [
        {"type": "model", "name": "local/qwen2.5-coder"},
        {"type": "tool", "name": "read_kyc_doc"},
        {"type": "tool", "name": "http_fetch", "scope": "egress",
         "allowed_endpoints": ["internal-kyc.bank"]},
    ],
    "controls": {"egress": "deny-by-default", "residency": "EU"},
}


def main():
    line("━")
    print("  ABOM — inline action gate")
    print("  what your SIEM logs, we BLOCK — and prove we blocked it")
    line("━")

    gate = Gate(SIGNED_MANIFEST, run_id="loan-doc-agent@1.4.0")

    # 1. A legitimate, declared action — allowed.
    print("\n[1] Agent reads a KYC document (declared tool)")
    d = gate.check(Action(tool="read_kyc_doc", args={"id": "cust-4471"}))
    print(f"    → {d.verdict.value.upper()}  ({d.rule})")

    # 2. Declared egress to an approved endpoint — allowed.
    print("\n[2] Agent fetches from internal-kyc.bank (allowed endpoint)")
    d = gate.check(Action(tool="http_fetch", endpoint="internal-kyc.bank"))
    print(f"    → {d.verdict.value.upper()}  ({d.rule})")

    # 3. PROMPT INJECTION: agent tries to move money via an undeclared tool.
    print("\n[3] ⚠  Prompt injection: agent calls wire_transfer($1,000,000)")
    print("        (wire_transfer is NOT in the signed manifest)")

    @gate.gated()
    def wire_transfer(amount, to):
        # This body must NEVER run.
        return f"WIRED ${amount:,} to {to}"

    try:
        wire_transfer(1_000_000, to="attacker-iban")
        print("    → !!! action executed — GATE FAILED !!!")
    except ActionDenied as e:
        dec = e.decision
        print(f"    → {dec.verdict.value.upper()}  ({dec.rule})")
        print(f"      {dec.reason}")
        print("      the money never moved — the call was blocked before execution")

    # 4. Egress to an unapproved endpoint — denied.
    print("\n[4] Agent tries http_fetch → evil.example.com (not on allowlist)")
    d = gate.check(Action(tool="http_fetch", endpoint="evil.example.com"))
    print(f"    → {d.verdict.value.upper()}  ({d.rule})")

    # ---- the notarized record an auditor receives ----
    line()
    print("  THE NOTARIZED RECORD (what the auditor verifies)")
    line()
    head = gate.head()
    print(f"  decisions notarized : {len(gate.decisions)}")
    print(f"  transparency log    : size={head['tree_size']}  root={head['root_hash'][:24]}…")

    denial = next(d for d in gate.decisions if d.rule == "tool_not_in_manifest")

    # An independent party verifies WITHOUT trusting the operator:
    entry = canonical_json({
        "run_id": gate.run_id, "seq": denial.seq, "verdict": denial.verdict.value,
        "rule": denial.rule, "action": denial.action, "created_at": denial.created_at,
    }).encode("utf-8")

    incl_ok = verify_inclusion_hex(entry, denial.inclusion_proof)
    head_bytes = canonical_json(denial.tree_head).encode("utf-8")
    sig_ok = verify_payload(head_bytes, denial.head_signature)

    print(f"\n  auditor checks the wire_transfer denial (seq {denial.seq}):")
    print(f"    inclusion proof valid : {incl_ok}   (it IS in the signed log)")
    print(f"    tree-head signature   : {sig_ok}   (signed by {denial.head_signature['backend']} key "
          f"{denial.head_signature['key_id']})")

    # ---- tamper attempt: try to erase the denial ----
    print("\n  attacker tries to backdate the record (flip DENY → allow):")
    forged = canonical_json({
        "run_id": gate.run_id, "seq": denial.seq, "verdict": "allow",
        "rule": "manifest_allows", "action": denial.action, "created_at": denial.created_at,
    }).encode("utf-8")
    tampered_ok = verify_inclusion_hex(forged, denial.inclusion_proof)
    print(f"    forged record included : {tampered_ok}   (rejected — the proof no longer matches)")

    line("━")
    verdict = gate.verify()
    print(f"  full log re-verification: valid={verdict['valid']} "
          f"over {verdict['decisions']} decisions")
    print("  → blocked the action, signed the proof, and it survives tampering.")
    line("━")


if __name__ == "__main__":
    main()
