"""Runtime-provenance policy checks in bom.verify_abom (the `verify --run` core).

These branches — composition-match, model allowlist / drift, residency egress,
egress allowlist, approval coverage, and chain-integrity — decide whether a run's
Action Provenance actually honoured the signed envelope. They were previously
untested at the chain level.
"""
from abom import bom


def _manifest(models=("gpt-4o",), tools=("http_fetch",)):
    components = [{"type": "model", "name": m} for m in models]
    components += [{"type": "tool", "name": t} for t in tools]
    return bom.build_composition(
        agent={"name": "a", "version": "1"}, components=components, controls={}
    )


def _chain(manifest, **action):
    chain = bom.new_chain()
    bom.append_action(
        chain,
        composition_sha256=manifest["composition_sha256"],
        agent_ref="a@1",
        decision="did a thing",
        **action,
    )
    return chain


def test_clean_run_passes():
    m = _manifest()
    chain = _chain(m, model_calls=[{"model": "gpt-4o"}],
                   tools_invoked=[{"name": "http_fetch", "endpoint": "internal-kyc.bank"}])
    out = bom.verify_abom(m, chain,
                          policy={"allowed_models": ["gpt-4o"],
                                  "allowed_egress_endpoints": ["internal-kyc.bank"]})
    assert out["ok"] is True
    assert out["actions"] == 1


def test_unapproved_model_in_run_flagged():
    m = _manifest(models=("gpt-4o",))
    chain = _chain(m, model_calls=[{"model": "gpt-4o"}])
    out = bom.verify_abom(m, chain, policy={"allowed_models": ["only-this"]})
    assert any(f["rule"] == "model_allowlist" and f.get("seq") == 0 for f in out["findings"])


def test_composition_drift_when_runtime_model_undeclared():
    m = _manifest(models=("gpt-4o",))
    # runtime uses a model that is NOT in the signed manifest
    chain = _chain(m, model_calls=[{"model": "claude-3-5-sonnet"}])
    out = bom.verify_abom(m, chain)
    assert any(f["rule"] == "composition_drift" for f in out["findings"])


def test_residency_egress_flagged():
    m = _manifest()
    chain = _chain(m, data_touched=[{"classification": "confidential", "egress": True}])
    out = bom.verify_abom(m, chain, policy={"no_egress_classifications": ["confidential"]})
    assert any(f["rule"] == "residency" for f in out["findings"])


def test_egress_to_unapproved_endpoint_flagged():
    m = _manifest()
    chain = _chain(m, tools_invoked=[{"name": "http_fetch", "endpoint": "evil.example.com"}])
    out = bom.verify_abom(m, chain, policy={"allowed_egress_endpoints": ["internal-kyc.bank"]})
    assert any(f["rule"] == "egress_allowlist" for f in out["findings"])


def test_consequential_action_without_approval_flagged():
    m = _manifest()
    chain = _chain(m, policy_decisions=[{"rule": "approval_required", "result": "required"}])
    out = bom.verify_abom(m, chain)
    assert any(f["rule"] == "approval_coverage" for f in out["findings"])


def test_consequential_action_with_approval_passes():
    m = _manifest()
    chain = _chain(m,
                   policy_decisions=[{"rule": "approval_required", "result": "required"}],
                   approval={"by": "op@bank", "decision": "approved"})
    out = bom.verify_abom(m, chain)
    assert not any(f["rule"] == "approval_coverage" for f in out["findings"])


def test_composition_mismatch_flagged():
    m = _manifest()
    chain = bom.new_chain()
    bom.append_action(chain, composition_sha256="deadbeef" * 8, agent_ref="a@1",
                      decision="x", model_calls=[{"model": "gpt-4o"}])
    out = bom.verify_abom(m, chain)
    assert any(f["rule"] == "composition_match" for f in out["findings"])


def test_broken_chain_flagged():
    m = _manifest()
    chain = _chain(m, model_calls=[{"model": "gpt-4o"}])
    chain[0]["hash"] = "tampered"  # break the hash chain
    out = bom.verify_abom(m, chain)
    assert any(f["rule"] == "chain_integrity" for f in out["findings"])


def test_sign_wrapper_roundtrips():
    signed = bom.sign({"a": 1})
    assert bom.verify_signature(signed) is True
