"""Deterministic and property-based tests for proposal apply rollback evidence."""

from __future__ import annotations

import copy
import hashlib
import hmac
import json
import string
import sys
from datetime import datetime, timezone
from pathlib import Path

import pytest
from hypothesis import given, settings
from hypothesis import strategies as st

REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))

import apply_proposal as apply  # noqa: E402
import proposal_apply_lifecycle as lifecycle  # noqa: E402

PROPOSAL_ID = "99999999-9999-4999-8999-999999999999"
CLAIM_A = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
CLAIM_B = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
SOURCE_ID = "cccccccc-cccc-4ccc-8ccc-cccccccccccc"
TOOL_ID = "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
AGENT_ID = "11111111-1111-4111-8111-111111111111"
APPLIED_AT = "2026-07-15T10:15:00+00:00"
REVIEWED_AT = "2026-07-15T09:30:00+00:00"
ROW_FAMILIES = tuple(lifecycle.ROW_TABLES)


@pytest.fixture(autouse=True)
def _trusted_attestation_keys(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    authorization_key = tmp_path / "authorization-attestation.key"
    action_readback_key = tmp_path / "action-readback-attestation.key"
    authorization_key.write_bytes(b"authorization-attestation-test-key-0001")
    action_readback_key.write_bytes(b"action-readback-attestation-test-key-0002")
    authorization_key.chmod(0o600)
    action_readback_key.chmod(0o600)
    monkeypatch.setattr(lifecycle, "DEFAULT_AUTHORIZATION_ATTESTATION_KEY_PATH", authorization_key)
    monkeypatch.setattr(lifecycle, "DEFAULT_ACTION_READBACK_ATTESTATION_KEY_PATH", action_readback_key)


def _apply_payload() -> dict:
    return {
        "contract_version": 2,
        "agent_id": AGENT_ID,
        "claims": [
            {
                "id": CLAIM_A,
                "type": "structural",
                "text": "Canonical claim A",
                "status": "open",
                "confidence": 0.8,
                "tags": ["working-leo", "lifecycle"],
                "created_by": AGENT_ID,
            },
            {
                "id": CLAIM_B,
                "type": "concept",
                "text": "Canonical claim B",
                "status": "open",
                "confidence": None,
                "tags": [],
                "created_by": AGENT_ID,
            },
        ],
        "sources": [
            {
                "id": SOURCE_ID,
                "source_type": "paper",
                "url": "https://example.test/lifecycle",
                "storage_path": None,
                "excerpt": "Bound lifecycle evidence.",
                "hash": "sha256:lifecycle-source",
                "created_by": AGENT_ID,
            }
        ],
        "evidence": [
            {
                "claim_id": CLAIM_A,
                "source_id": SOURCE_ID,
                "role": "grounds",
                "weight": 0.9,
                "created_by": AGENT_ID,
            }
        ],
        "edges": [
            {
                "from_claim": CLAIM_A,
                "to_claim": CLAIM_B,
                "edge_type": "supports",
                "weight": 0.7,
                "created_by": AGENT_ID,
            }
        ],
        "reasoning_tools": [
            {
                "id": TOOL_ID,
                "agent_id": AGENT_ID,
                "name": "Lifecycle verifier",
                "description": "Checks the apply and compensating rollback boundary.",
                "category": "verification",
            }
        ],
    }


def _proposal_before() -> dict:
    return {
        "id": PROPOSAL_ID,
        "proposal_type": "approve_claim",
        "status": "approved",
        "payload": {"apply_payload": _apply_payload()},
        "reviewed_by_handle": "independent-reviewer",
        "reviewed_by_agent_id": None,
        "reviewed_at": REVIEWED_AT,
        "review_note": "Approved this exact strict payload.",
        "applied_by_handle": None,
        "applied_by_agent_id": None,
        "applied_at": None,
    }


def _approval_snapshot(proposal: dict) -> dict:
    return {
        "proposal_id": proposal["id"],
        "proposal_type": proposal["proposal_type"],
        "payload": copy.deepcopy(proposal["payload"]),
        "reviewed_by_handle": proposal["reviewed_by_handle"],
        "reviewed_by_agent_id": proposal["reviewed_by_agent_id"],
        "reviewed_by_db_role": "kb_reviewer",
        "reviewed_at": proposal["reviewed_at"],
        "review_note": proposal["review_note"],
    }


def _canonical_rows() -> dict[str, list[dict]]:
    edge_id = apply.deterministic_row_uuid(PROPOSAL_ID, "claim_edge", CLAIM_A, CLAIM_B, "supports")
    return {
        "claims": [
            {
                "id": CLAIM_A,
                "type": "structural",
                "text": "Canonical claim A",
                "status": "open",
                "confidence": 0.8,
                "tags": ["working-leo", "lifecycle"],
                "created_by": AGENT_ID,
                "superseded_by": None,
                "created_at": APPLIED_AT,
                "updated_at": APPLIED_AT,
            },
            {
                "id": CLAIM_B,
                "type": "concept",
                "text": "Canonical claim B",
                "status": "open",
                "confidence": None,
                "tags": [],
                "created_by": AGENT_ID,
                "superseded_by": None,
                "created_at": APPLIED_AT,
                "updated_at": APPLIED_AT,
            },
        ],
        "sources": [
            {
                "id": SOURCE_ID,
                "source_type": "paper",
                "url": "https://example.test/lifecycle",
                "storage_path": None,
                "excerpt": "Bound lifecycle evidence.",
                "hash": "sha256:lifecycle-source",
                "created_by": AGENT_ID,
                "created_at": APPLIED_AT,
            }
        ],
        "claim_evidence": [
            {
                "claim_id": CLAIM_A,
                "source_id": SOURCE_ID,
                "role": "grounds",
                "weight": 0.9,
                "created_by": AGENT_ID,
                "created_at": APPLIED_AT,
            }
        ],
        "claim_edges": [
            {
                "id": edge_id,
                "from_claim": CLAIM_A,
                "to_claim": CLAIM_B,
                "edge_type": "supports",
                "weight": 0.7,
                "created_by": AGENT_ID,
                "created_at": APPLIED_AT,
            }
        ],
        "reasoning_tools": [
            {
                "id": TOOL_ID,
                "agent_id": AGENT_ID,
                "name": "Lifecycle verifier",
                "description": "Checks the apply and compensating rollback boundary.",
                "category": "verification",
                "created_at": APPLIED_AT,
                "updated_at": APPLIED_AT,
            }
        ],
    }


def _empty_targets() -> dict[str, list[dict]]:
    return {family: [] for family in ROW_FAMILIES}


def _apply_receipt(proposal: dict, rows: dict[str, list[dict]], preflight: dict) -> dict:
    applied = copy.deepcopy(proposal)
    applied.update(
        {
            "status": "applied",
            "applied_by_handle": apply.SERVICE_AGENT_HANDLE,
            "applied_by_agent_id": AGENT_ID,
            "applied_at": APPLIED_AT,
        }
    )
    apply_sql = lifecycle.build_fresh_owned_apply_sql(proposal, preflight, apply.SERVICE_AGENT_HANDLE)
    return apply.replay_receipt.build_replay_receipt(
        applied,
        copy.deepcopy(rows),
        apply_sql_sha256=lifecycle.sha256_text(apply_sql),
        apply_sql_source="exact_executed_sql",
        exported_at_utc="2026-07-15T10:16:00+00:00",
    )


def _materials() -> tuple[dict, dict, dict, dict, str]:
    proposal = _proposal_before()
    approval = _approval_snapshot(proposal)
    preflight = lifecycle.build_fresh_preflight(
        proposal,
        _empty_targets(),
        captured_at_utc="2026-07-15T10:00:00+00:00",
    )
    receipt = _apply_receipt(proposal, _canonical_rows(), preflight)
    rollback_sql = lifecycle.build_compensating_rollback_sql(proposal, approval, preflight, receipt)
    return proposal, approval, preflight, receipt, rollback_sql


def _passing_lifecycle_receipt() -> dict:
    proposal, approval, preflight, receipt, rollback_sql = _materials()
    proposal_after = copy.deepcopy(proposal)
    proposal_after.update(
        {
            "status": "canceled",
            "applied_by_handle": None,
            "applied_by_agent_id": None,
            "applied_at": None,
        }
    )
    return lifecycle.build_lifecycle_receipt(
        proposal_before=proposal,
        approval_before=approval,
        preflight=preflight,
        apply_receipt=receipt,
        rollback_sql=rollback_sql,
        proposal_after=proposal_after,
        approval_after=copy.deepcopy(approval),
        target_rows_after=_empty_targets(),
        counts_before={family: 7 + index for index, family in enumerate(ROW_FAMILIES)},
        counts_after={family: 7 + index for index, family in enumerate(ROW_FAMILIES)},
        unrelated_before={"fingerprint": "same", "row_count": 17},
        unrelated_after={"fingerprint": "same", "row_count": 17},
        rollback_replay_refused=True,
        generated_at_utc="2026-07-15T10:30:00+00:00",
    )


def _destination(*, candidate_present: bool = True) -> dict:
    lifecycle_receipt = _passing_lifecycle_receipt()
    candidates = []
    if candidate_present:
        candidates.append(
            {
                "id": PROPOSAL_ID,
                "proposal_type": "approve_claim",
                "status": "approved",
                "applied_at": None,
                "proposal_payload_sha256": lifecycle_receipt["proposal_payload_sha256"],
                "apply_payload_sha256": lifecycle_receipt["apply_payload_sha256"],
                "approval_snapshot_sha256": lifecycle_receipt["approval_snapshot_sha256"],
                "reviewed_by_handle": "independent-reviewer",
                "reviewed_by_agent_id": None,
                "reviewed_at": REVIEWED_AT,
                "review_note": "Approved this exact strict payload.",
                "immutable_approval_exact": True,
            }
        )
    return {
        "observed_at_utc": "2026-07-15T10:45:00+00:00",
        "read_only_transaction": True,
        "container": "teleo-pg",
        "database": "teleo",
        "system_identifier": "7649789040005668902",
        "server_version_num": "160014",
        "approval_gate": {
            "immutable_approval_table_present": True,
            "approve_function_present": True,
            "assert_function_present": True,
            "finish_function_present": True,
            "review_role_present": True,
            "apply_role_present": True,
        },
        "approved_strict_candidates": candidates,
        "downstream_dependency_count": 0,
    }


def _authorization_materials() -> tuple[dict, dict, dict, dict]:
    proposal, approval, preflight, apply_receipt, _production_rollback_sql = _materials()
    receipt = _passing_lifecycle_receipt()
    destination = _destination()
    destination_identity = {
        key: destination[key] for key in ("container", "database", "system_identifier", "server_version_num")
    }
    production_rollback_artifact = lifecycle.build_production_rollback_artifact(
        proposal_before=proposal,
        approval_snapshot=approval,
        preflight=preflight,
        apply_receipt=apply_receipt,
        destination=destination_identity,
        generated_at_utc="2026-07-15T10:49:00+00:00",
    )
    packet = lifecycle.build_authorization_packet(
        receipt,
        destination,
        repo_git_sha=lifecycle.current_git_sha(REPO_ROOT),
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
        production_rollback_artifact=production_rollback_artifact,
        generated_at_utc="2026-07-15T10:50:00+00:00",
    )
    record = {
        "authorized": True,
        "rollback_authorized": True,
        "binding_sha256": packet["binding_sha256"],
        "production_rollback_sql_sha256": production_rollback_artifact["rollback_sql_sha256"],
        "operator_identity": "release-operator@example.test",
        "received_at_utc": "2026-07-15T11:00:00+00:00",
        "expires_at_utc": "2026-07-15T13:00:00+00:00",
    }
    destination["observed_at_utc"] = "2026-07-15T11:30:00+00:00"
    record["authorization_text"] = lifecycle.expected_authorization_text(packet, record)
    authorization_key = lifecycle.DEFAULT_AUTHORIZATION_ATTESTATION_KEY_PATH.read_bytes().strip()
    action_readback_key = lifecycle.DEFAULT_ACTION_READBACK_ATTESTATION_KEY_PATH.read_bytes().strip()
    record["authorization_provenance"] = {
        "scheme": lifecycle.ATTESTATION_SCHEME,
        "source": "codex_platform_user_message",
        "source_event_id": "codex-user-event-019f-test",
        "source_event_sha256": lifecycle.sha256_text(record["authorization_text"]),
        "key_id": hashlib.sha256(authorization_key).hexdigest(),
        "attestation_hmac_sha256": "",
    }
    record["authorization_provenance"]["attestation_hmac_sha256"] = hmac.new(
        authorization_key,
        lifecycle.canonical_json(lifecycle.authorization_attestation_payload(packet, record)).encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    record["action_readback_provenance"] = {
        "scheme": lifecycle.ATTESTATION_SCHEME,
        "collector": "teleo_production_readonly_collector",
        "receipt_id": "production-readback-019f-test",
        "observed_destination_sha256": lifecycle.sha256_json(lifecycle.action_readback_projection(destination)),
        "key_id": hashlib.sha256(action_readback_key).hexdigest(),
        "attestation_hmac_sha256": "",
    }
    record["action_readback_provenance"]["attestation_hmac_sha256"] = hmac.new(
        action_readback_key,
        lifecycle.canonical_json(lifecycle.action_readback_attestation_payload(packet, destination, record)).encode(
            "utf-8"
        ),
        hashlib.sha256,
    ).hexdigest()
    return packet, record, destination, production_rollback_artifact


def test_fresh_preflight_requires_every_owned_target_to_be_absent_and_is_deterministic():
    proposal = _proposal_before()
    first = lifecycle.build_fresh_preflight(
        proposal,
        _empty_targets(),
        captured_at_utc="2026-07-15T10:00:00+00:00",
    )
    second = lifecycle.build_fresh_preflight(
        proposal,
        _empty_targets(),
        captured_at_utc="2026-07-15T10:01:00+00:00",
    )

    assert first["fresh_owned_targets"] is True
    assert first["preflight_sha256"] == second["preflight_sha256"]
    assert first["preflight_artifact_sha256"] != second["preflight_artifact_sha256"]
    assert first["captured_at_utc"] != second["captured_at_utc"]

    nonfresh = _empty_targets()
    nonfresh["claims"] = [{"id": CLAIM_A}]
    with pytest.raises(ValueError, match="absent target rows"):
        lifecycle.build_fresh_preflight(proposal, nonfresh)

    incomplete = _empty_targets()
    incomplete.pop("claim_edges")
    with pytest.raises(ValueError, match="every approve_claim target table"):
        lifecycle.build_fresh_preflight(proposal, incomplete)


def test_compensating_rollback_refuses_immutable_v3_admissions() -> None:
    proposal = _proposal_before()
    proposal["payload"]["apply_payload"]["contract_version"] = 3
    with pytest.raises(ValueError, match="V3 admitted rows are immutable"):
        lifecycle.build_fresh_preflight(proposal, _empty_targets())


def test_fresh_owned_apply_consumes_timestamped_preflight_and_never_adopts_conflicts():
    proposal, _approval, preflight, _receipt, _rollback_sql = _materials()

    strict_sql = lifecycle.build_fresh_owned_apply_sql(proposal, preflight, apply.SERVICE_AGENT_HANDLE)
    ordinary_sql = apply.build_apply_sql(proposal, apply.SERVICE_AGENT_HANDLE)

    assert preflight["preflight_artifact_sha256"] in strict_sql
    assert "set transaction isolation level serializable" in strict_sql.lower()
    assert "proposal-fresh-apply:" in strict_sql
    assert "on conflict" not in strict_sql.lower()
    assert "where not exists" not in strict_sql.lower()
    assert "on conflict" in ordinary_sql.lower()

    stale = copy.deepcopy(preflight)
    stale["captured_at_utc"] = "1970-01-01T00:00:00+00:00"
    with pytest.raises(ValueError, match="artifact hash"):
        lifecycle.build_fresh_owned_apply_sql(proposal, stale, apply.SERVICE_AGENT_HANDLE)


@pytest.mark.parametrize(
    "mutation",
    (
        "artifact",
        "schema",
        "captured_at_utc",
        "proposal_id",
        "proposal_payload_sha256",
        "apply_payload_sha256",
        "target_family_missing",
        "target_row_present",
        "fresh_flag_type",
        "state_hash",
        "artifact_hash",
        "extra_field",
    ),
)
def test_rollback_refuses_every_preflight_contract_mutation(mutation: str):
    proposal, approval, preflight, receipt, _rollback_sql = _materials()
    changed = copy.deepcopy(preflight)
    if mutation == "artifact":
        changed["artifact"] = "proposal_apply_fresh_preflight_tampered"
    elif mutation == "schema":
        changed["schema"] = "livingip.proposalApplyFreshPreflight.v0"
    elif mutation == "captured_at_utc":
        changed["captured_at_utc"] = "2026-07-15T10:00:01+00:00"
    elif mutation == "proposal_id":
        changed["proposal_id"] = CLAIM_A
    elif mutation == "proposal_payload_sha256":
        changed["proposal_payload_sha256"] = "0" * 64
    elif mutation == "apply_payload_sha256":
        changed["apply_payload_sha256"] = "0" * 64
    elif mutation == "target_family_missing":
        changed["target_rows_before"].pop("claim_edges")
    elif mutation == "target_row_present":
        changed["target_rows_before"]["claims"] = [{"id": CLAIM_A}]
    elif mutation == "fresh_flag_type":
        changed["fresh_owned_targets"] = 1
    elif mutation == "state_hash":
        changed["preflight_sha256"] = "0" * 64
    elif mutation == "artifact_hash":
        changed["preflight_artifact_sha256"] = "0" * 64
    else:
        changed["unexpected"] = True

    with pytest.raises(ValueError):
        lifecycle.build_compensating_rollback_sql(proposal, approval, changed, receipt)


def test_compensating_rollback_is_atomic_deterministic_and_refuses_state_drift():
    proposal, approval, preflight, receipt, rollback_sql = _materials()

    assert rollback_sql == lifecycle.build_compensating_rollback_sql(proposal, approval, preflight, receipt)
    assert rollback_sql.startswith("begin;")
    assert rollback_sql.rstrip().endswith("commit;")
    assert "proposal rollback drift:" in rollback_sql
    assert "to_jsonb(t)" in rollback_sql
    assert rollback_sql.index("proposal rollback drift:") < rollback_sql.index("delete from public.claim_evidence")
    delete_positions = [
        rollback_sql.index(f"delete from {lifecycle.ROW_TABLES[family]}") for family in lifecycle.DELETE_ORDER
    ]
    assert delete_positions == sorted(delete_positions)
    assert "set status = 'canceled'" in rollback_sql
    assert "downstream_dependency_count" in rollback_sql
    assert "select ce.claim_id" in rollback_sql
    assert "select ce.id" not in rollback_sql
    assert "downstream dependenc(ies) exist" in rollback_sql
    assert "immutable approval snapshot drifted" in rollback_sql

    drifted_receipt = copy.deepcopy(receipt)
    drifted_receipt["proposal"]["payload"]["apply_payload"]["claims"][0]["text"] = "drift"
    with pytest.raises(ValueError, match="payload does not match preflight"):
        lifecycle.build_compensating_rollback_sql(proposal, approval, preflight, drifted_receipt)


@given(
    suffix=st.text(
        alphabet=string.ascii_letters + string.digits + " -_",
        min_size=1,
        max_size=32,
    )
)
@settings(max_examples=30, deadline=None)
def test_rollback_refuses_any_canonical_row_tampering_without_rebuilt_receipt(suffix: str):
    proposal, approval, preflight, receipt, _rollback_sql = _materials()
    changed = copy.deepcopy(receipt)
    changed["canonical_rows"]["claims"][0]["text"] = f"mutated:{suffix}"

    with pytest.raises(ValueError, match="replay-contract validation"):
        lifecycle.build_compensating_rollback_sql(proposal, approval, preflight, changed)


@pytest.mark.parametrize(
    "mutation",
    ("contract_bool", "expected_count_bool", "actual_count_float", "check_integer"),
)
def test_replay_receipt_json_types_are_exact_not_python_equal(mutation: str):
    proposal, approval, preflight, receipt, _rollback_sql = _materials()
    changed = copy.deepcopy(receipt)
    if mutation == "contract_bool":
        changed["receipt_contract_version"] = True
    elif mutation == "expected_count_bool":
        changed["expected_row_counts"]["sources"] = True
    elif mutation == "actual_count_float":
        changed["actual_row_counts"]["sources"] = 1.0
    else:
        changed["checks"]["proposal_applied"] = 1

    with pytest.raises(ValueError, match="replay-contract validation"):
        lifecycle.build_compensating_rollback_sql(proposal, approval, preflight, changed)


def test_rollback_sql_hash_binds_every_receipt_observed_field():
    proposal, approval, preflight, receipt, rollback_sql = _materials()
    changed_rows = copy.deepcopy(receipt["canonical_rows"])
    changed_rows["claims"][0]["created_at"] = "2026-07-15T10:15:00.123456+00:00"
    changed_receipt = _apply_receipt(proposal, changed_rows, preflight)

    changed_sql = lifecycle.build_compensating_rollback_sql(
        proposal,
        approval,
        preflight,
        changed_receipt,
    )

    assert lifecycle.sha256_text(changed_sql) != lifecycle.sha256_text(rollback_sql)
    assert "to_jsonb(t)" in changed_sql
    assert "proposal rollback drift: claims[0] row does not match apply receipt" in changed_sql


def test_fresh_apply_receipt_requires_deterministic_generated_edge_ids():
    proposal, approval, preflight, receipt, _rollback_sql = _materials()
    changed_rows = copy.deepcopy(receipt["canonical_rows"])
    changed_rows["claim_edges"][0]["id"] = "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee"
    changed_receipt = _apply_receipt(proposal, changed_rows, preflight)

    with pytest.raises(ValueError, match="is not deterministic"):
        lifecycle.build_compensating_rollback_sql(
            proposal,
            approval,
            preflight,
            changed_receipt,
        )


def test_fresh_apply_receipt_uses_composite_claim_evidence_key_without_id():
    proposal, approval, preflight, receipt, _rollback_sql = _materials()
    evidence = receipt["canonical_rows"]["claim_evidence"][0]

    assert "id" not in evidence
    assert lifecycle.canonical_row_key("claim_evidence", evidence) == f"{CLAIM_A}|{SOURCE_ID}|grounds"

    changed_receipt = copy.deepcopy(receipt)
    changed_receipt["canonical_rows"]["claim_evidence"].append(copy.deepcopy(evidence))
    with pytest.raises(ValueError, match="unique canonical keys"):
        lifecycle.build_compensating_rollback_sql(
            proposal,
            approval,
            preflight,
            changed_receipt,
        )


def test_production_rollback_artifact_reconstructs_exact_sql_and_rejects_noop_substitution():
    proposal, approval, preflight, apply_receipt, rollback_sql = _materials()
    destination = {
        "container": "teleo-pg",
        "database": "teleo",
        "system_identifier": "7649789040005668902",
        "server_version_num": "160014",
    }
    artifact = lifecycle.build_production_rollback_artifact(
        proposal_before=proposal,
        approval_snapshot=approval,
        preflight=preflight,
        apply_receipt=apply_receipt,
        destination=destination,
        generated_at_utc="2026-07-15T10:49:00+00:00",
    )

    assert (
        lifecycle.validate_production_rollback_artifact(
            artifact,
            expected_destination=destination,
        )
        == artifact
    )
    assert artifact["rollback_sql"] == rollback_sql

    noop = copy.deepcopy(artifact)
    noop["rollback_sql"] = (
        "begin; -- proposal rollback drift: -- immutable approval snapshot drifted "
        "-- set status = 'canceled'\ncommit;\n"
    )
    noop["rollback_sql_sha256"] = lifecycle.sha256_text(noop["rollback_sql"])
    unsigned = dict(noop)
    unsigned.pop("artifact_sha256")
    noop["artifact_sha256"] = lifecycle.sha256_json(unsigned)

    with pytest.raises(ValueError, match="reconstructed exact SQL"):
        lifecycle.validate_production_rollback_artifact(noop, expected_destination=destination)


def test_lifecycle_receipt_proves_inverse_and_has_self_consistent_hashes():
    receipt = _passing_lifecycle_receipt()
    unsigned = dict(receipt)
    receipt_hash = unsigned.pop("lifecycle_receipt_sha256")

    assert receipt["pass"] is True
    assert receipt["current_tier"] == "T2_runtime"
    assert all(receipt["checks"].values())
    assert all(not rows for rows in receipt["rollback"]["target_rows_after"].values())
    assert receipt["rollback"]["counts_after_rollback"] == receipt["rollback"]["counts_before_apply"]
    assert receipt["production_apply_executed"] is False
    assert receipt["production_apply_authorization_present"] is False
    assert receipt_hash == lifecycle.sha256_json(unsigned)


def test_lifecycle_receipt_downgrades_when_unrelated_state_changes():
    proposal, approval, preflight, receipt, rollback_sql = _materials()
    result = lifecycle.build_lifecycle_receipt(
        proposal_before=proposal,
        approval_before=approval,
        preflight=preflight,
        apply_receipt=receipt,
        rollback_sql=rollback_sql,
        proposal_after=copy.deepcopy(proposal),
        approval_after=copy.deepcopy(approval),
        target_rows_after=_empty_targets(),
        counts_before={family: 1 for family in ROW_FAMILIES},
        counts_after={family: 1 for family in ROW_FAMILIES},
        unrelated_before={"fingerprint": "before"},
        unrelated_after={"fingerprint": "after"},
        rollback_replay_refused=True,
    )

    assert result["pass"] is False
    assert result["current_tier"] == "T1_model"
    assert result["checks"]["unrelated_rows_unchanged"] is False


def test_retained_current_lifecycle_evidence_is_self_consistent():
    report_dir = REPO_ROOT / "docs" / "reports" / "leo-working-state-20260709"
    canary = json.loads((report_dir / "proposal-apply-lifecycle-canary-current.json").read_text(encoding="utf-8"))
    receipt = json.loads((report_dir / "proposal-apply-lifecycle-receipt-current.json").read_text(encoding="utf-8"))
    rollback = (report_dir / "proposal-apply-clone-rollback-current.sql").read_text(encoding="utf-8")
    unsigned = dict(receipt)
    receipt_sha256 = unsigned.pop("lifecycle_receipt_sha256")

    assert canary["status"] == "pass"
    assert canary["current_tier"] == "T3_live_readonly"
    assert canary["runtime_scope"] == "isolated_postgres_container_from_readonly_live_schema"
    assert canary["applied_rollback_receipt"] == receipt
    outer = canary["outer_isolation"]
    assert outer["source_counts_before"] == outer["source_counts_after"]
    assert outer["source_counts_before"]["public.claims"] == 1837
    assert outer["source_counts_before"]["public.claim_evidence"] == 4670
    assert outer["source_counts_before"]["kb_stage.kb_proposals"] == 29
    assert outer["service_stable"] is True
    assert outer["cleanup_readback"]["label_scoped_orphan_count"] == 0
    assert all(delta == 0 for delta in outer["canonical_table_deltas"].values())
    assert canary["checks"]["intervening_exact_row_refused_without_adoption"] is True
    assert canary["checks"]["rollback_refuses_downstream_dependencies_without_deletion"] is True
    assert canary["checks"]["post_rollback_worker_reapply_refused"] is True
    assert receipt["pass"] is True
    assert receipt["production_apply_executed"] is False
    assert receipt_sha256 == lifecycle.sha256_json(unsigned)
    assert hashlib.sha256(rollback.encode("utf-8")).hexdigest() == receipt["rollback"]["rollback_sql_sha256"]


def test_retained_production_authorization_packet_is_exact_and_unexecuted():
    packet_path = (
        REPO_ROOT
        / "docs"
        / "reports"
        / "leo-working-state-20260709"
        / "proposal-apply-production-authorization-packet-current.json"
    )
    packet = json.loads(packet_path.read_text(encoding="utf-8"))

    assert packet["binding_sha256"] == lifecycle.sha256_json(packet["binding"])
    assert packet["binding"]["engine"]["apply_engine_path"] == "scripts/apply_proposal.py"
    assert packet["binding"]["approval_gate_dependency"]["path"] == "scripts/kb_apply_prereqs.sql"
    assert packet["production_preconditions"]["approval_gate_ready"] is False
    assert packet["production_preconditions"]["strict_proposal_present_and_approved"] is False
    assert packet["production_preconditions"]["exact_production_rollback_packet_ready"] is False
    assert packet["safe_to_enter_apply_window"] is False
    assert packet["production_apply_authorization_present"] is False
    assert packet["production_apply_executed"] is False
    assert all(value is False for value in packet["scope_exclusions"].values())
    assert "/private/tmp" not in json.dumps(packet, sort_keys=True)


def test_authorization_packet_is_exact_but_never_self_authorizes_production():
    packet, record, destination, production_rollback_artifact = _authorization_materials()
    verification = lifecycle.verify_authorization_record(
        packet,
        record,
        observed_destination=destination,
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
        production_rollback_artifact=production_rollback_artifact,
        now=datetime(2026, 7, 15, 12, 0, tzinfo=timezone.utc),
    )

    assert packet["required_tier"] == "T6_authorized_production"
    assert packet["current_tier"] == "T3_live_readonly"
    assert packet["safe_to_enter_apply_window"] is False
    assert packet["production_apply_authorization_present"] is False
    assert packet["production_apply_executed"] is False
    assert verification["safe_to_enter_apply_window"] is True
    assert verification["production_apply_executed"] is False
    assert all(verification["checks"].values())


def test_locally_synthesized_authorization_record_and_readback_cannot_self_authorize():
    packet, record, destination, production_rollback_artifact = _authorization_materials()
    forged = copy.deepcopy(record)
    forged["operator_identity"] = "forged-by-local-caller"
    forged["authorization_text"] = lifecycle.expected_authorization_text(packet, forged)
    forged["authorization_provenance"]["source_event_sha256"] = lifecycle.sha256_text(forged["authorization_text"])
    forged["authorization_provenance"]["attestation_hmac_sha256"] = "0" * 64
    forged["action_readback_provenance"]["attestation_hmac_sha256"] = "0" * 64

    verification = lifecycle.verify_authorization_record(
        packet,
        forged,
        observed_destination=copy.deepcopy(destination),
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
        production_rollback_artifact=production_rollback_artifact,
        now=datetime(2026, 7, 15, 12, 0, tzinfo=timezone.utc),
    )

    assert verification["checks"]["authorization_provenance_independently_attested"] is False
    assert verification["checks"]["action_readback_provenance_independently_attested"] is False
    assert verification["safe_to_enter_apply_window"] is False


def test_action_time_downstream_dependency_is_bound_and_fails_closed():
    packet, record, destination, production_rollback_artifact = _authorization_materials()
    destination["downstream_dependency_count"] = 1

    verification = lifecycle.verify_authorization_record(
        packet,
        record,
        observed_destination=destination,
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
        production_rollback_artifact=production_rollback_artifact,
        now=datetime(2026, 7, 15, 12, 0, tzinfo=timezone.utc),
    )

    assert verification["checks"]["zero_downstream_dependencies_at_action"] is False
    assert verification["checks"]["action_readback_provenance_independently_attested"] is False
    assert verification["safe_to_enter_apply_window"] is False


def test_authorization_packet_names_missing_live_gate_before_requesting_auth(tmp_path):
    receipt = _passing_lifecycle_receipt()
    destination = _destination(candidate_present=False)
    destination["approval_gate"] = {key: False for key in destination["approval_gate"]}
    packet = lifecycle.build_authorization_packet(
        receipt,
        destination,
        repo_git_sha=lifecycle.current_git_sha(REPO_ROOT),
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
    )
    markdown_path = tmp_path / "authorization.md"
    lifecycle.write_authorization_markdown(markdown_path, packet)

    assert packet["production_preconditions"]["approval_gate_ready"] is False
    assert packet["production_preconditions"]["matching_candidate_count"] == 0
    assert packet["production_preconditions"]["exact_production_rollback_packet_ready"] is False
    assert packet["binding"]["production_rollback"]["rollback_sql_sha256"] is None
    assert lifecycle.sha256_json(packet["binding"]) == packet["binding_sha256"]
    assert packet["safe_to_enter_apply_window"] is False
    assert packet["production_apply_authorization_present"] is False
    assert packet["production_apply_executed"] is False
    assert packet["next_exact_action"].startswith("deploy and independently verify")
    assert "This text is not actionable" in markdown_path.read_text(encoding="utf-8")


@given(value=st.sampled_from([False, None, 0, 1, "true", "yes", [], {}]))
@settings(max_examples=8, deadline=None)
def test_authorized_field_requires_the_exact_boolean_true(value):
    packet, record, destination, production_rollback_artifact = _authorization_materials()
    record["authorized"] = value
    record["authorization_text"] = lifecycle.expected_authorization_text(packet, record)

    verification = lifecycle.verify_authorization_record(
        packet,
        record,
        observed_destination=destination,
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
        production_rollback_artifact=production_rollback_artifact,
        now=datetime(2026, 7, 15, 12, 0, tzinfo=timezone.utc),
    )

    assert verification["checks"]["authorized_is_exact_true"] is False
    assert verification["safe_to_enter_apply_window"] is False


AUTHORIZATION_MUTATIONS = (
    "packet_schema",
    "packet_readonly",
    "approval_gate_packet",
    "production_rollback_packet",
    "strict_candidate",
    "rollback_authorized",
    "production_rollback_sha256",
    "binding_sha256",
    "operator_identity",
    "authorization_window",
    "authorization_text",
    "destination_container",
    "destination_database",
    "destination_system_identifier",
    "destination_server_version",
    "approval_gate_action",
    "strict_candidate_action",
    "apply_engine_sha256",
    "approval_gate_sha256",
    "binding_proposal",
    "binding_gate_false",
    "binding_preconditions",
    "packet_safe_flag",
    "scope_exclusion",
    "authorization_template",
    "record_extra_field",
    "action_readback_stale",
    "production_rollback_binding",
)


@given(mutation=st.sampled_from(AUTHORIZATION_MUTATIONS))
@settings(max_examples=len(AUTHORIZATION_MUTATIONS), deadline=None)
def test_every_authorization_binding_field_fails_closed_when_mutated(mutation: str):
    packet, record, destination, production_rollback_artifact = _authorization_materials()
    expected_failed_check = {
        "packet_schema": "packet_schema_exact",
        "packet_readonly": "packet_destination_readback_was_read_only",
        "approval_gate_packet": "approval_gate_ready_at_packet",
        "production_rollback_packet": "exact_production_rollback_packet_ready",
        "strict_candidate": "strict_proposal_present_and_approved",
        "rollback_authorized": "rollback_authorized_is_exact_true",
        "production_rollback_sha256": "production_rollback_sql_sha256_exact",
        "binding_sha256": "binding_sha256_exact",
        "operator_identity": "operator_identity_present",
        "authorization_window": "authorization_window_valid",
        "authorization_text": "authorization_text_exact",
        "destination_container": "destination_identity_exact",
        "destination_database": "destination_identity_exact",
        "destination_system_identifier": "destination_identity_exact",
        "destination_server_version": "destination_identity_exact",
        "approval_gate_action": "approval_gate_identity_exact_at_action",
        "strict_candidate_action": "strict_proposal_exact_at_action",
        "apply_engine_sha256": "apply_engine_sha256_exact",
        "approval_gate_sha256": "approval_gate_prerequisite_sha256_exact",
        "binding_proposal": "packet_binding_sha256_self_consistent",
        "binding_gate_false": "approval_gate_ready_at_packet",
        "binding_preconditions": "packet_preconditions_bound_exactly",
        "packet_safe_flag": "packet_never_self_authorizes_or_expands_scope",
        "scope_exclusion": "packet_never_self_authorizes_or_expands_scope",
        "authorization_template": "authorization_text_template_exact",
        "record_extra_field": "authorization_record_contract_exact",
        "action_readback_stale": "destination_readback_fresh_and_read_only_at_action",
        "production_rollback_binding": "production_rollback_sql_sha256_exact",
    }[mutation]

    if mutation == "packet_schema":
        packet["schema"] = "livingip.proposalApplyAuthorizationPacket.v0"
    elif mutation == "packet_readonly":
        packet["production_preconditions"]["destination_readback_was_read_only"] = False
    elif mutation == "approval_gate_packet":
        packet["production_preconditions"]["approval_gate_ready"] = False
    elif mutation == "production_rollback_packet":
        packet["production_preconditions"]["exact_production_rollback_packet_ready"] = False
    elif mutation == "strict_candidate":
        packet["production_preconditions"]["strict_proposal_present_and_approved"] = False
    elif mutation == "rollback_authorized":
        record["rollback_authorized"] = False
    elif mutation == "production_rollback_sha256":
        record["production_rollback_sql_sha256"] = "f" * 64
    elif mutation == "binding_sha256":
        record["binding_sha256"] = "0" * 64
    elif mutation == "operator_identity":
        record["operator_identity"] = "  "
    elif mutation == "authorization_window":
        record["expires_at_utc"] = "2026-07-15T11:30:00+00:00"
    elif mutation == "authorization_text":
        record["authorization_text"] += " altered"
    elif mutation.startswith("destination_"):
        key = {
            "destination_container": "container",
            "destination_database": "database",
            "destination_system_identifier": "system_identifier",
            "destination_server_version": "server_version_num",
        }[mutation]
        destination[key] = f"wrong-{key}"
    elif mutation == "approval_gate_action":
        destination["approval_gate"]["review_role_present"] = False
    elif mutation == "strict_candidate_action":
        destination["approved_strict_candidates"] = []
    elif mutation == "apply_engine_sha256":
        packet["binding"]["engine"]["apply_engine_sha256"] = "0" * 64
    elif mutation == "approval_gate_sha256":
        packet["binding"]["approval_gate_dependency"]["sha256"] = "0" * 64
    elif mutation == "binding_proposal":
        packet["binding"]["proposal"]["id"] = CLAIM_A
    elif mutation == "binding_gate_false":
        packet["binding"]["approval_gate"]["review_role_present"] = False
    elif mutation == "binding_preconditions":
        packet["binding"]["production_preconditions"]["approval_gate_ready"] = False
    elif mutation == "packet_safe_flag":
        packet["safe_to_enter_apply_window"] = True
    elif mutation == "scope_exclusion":
        packet["scope_exclusions"]["telegram_send_authorized"] = True
    elif mutation == "authorization_template":
        packet["authorization_text_template"] += " Also authorize all other mutations."
    elif mutation == "record_extra_field":
        record["unexpected"] = True
    elif mutation == "action_readback_stale":
        destination["observed_at_utc"] = "2026-07-15T10:59:59+00:00"
    elif mutation == "production_rollback_binding":
        packet["binding"]["production_rollback"]["rollback_sql_sha256"] = "f" * 64

    verification = lifecycle.verify_authorization_record(
        packet,
        record,
        observed_destination=destination,
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
        production_rollback_artifact=production_rollback_artifact,
        now=datetime(2026, 7, 15, 12, 0, tzinfo=timezone.utc),
    )

    assert verification["checks"][expected_failed_check] is False
    assert verification["safe_to_enter_apply_window"] is False
    assert verification["production_apply_authorization_present"] is False


def test_false_bound_gate_cannot_pass_even_after_packet_and_record_are_rehashed():
    packet, record, destination, production_rollback_artifact = _authorization_materials()
    packet["binding"]["approval_gate"]["review_role_present"] = False
    destination["approval_gate"]["review_role_present"] = False
    packet["binding_sha256"] = lifecycle.sha256_json(packet["binding"])
    packet["authorization_record_required"]["binding_sha256"] = packet["binding_sha256"]
    packet["authorization_text_template"] = lifecycle._authorization_text_template(
        packet["binding"],
        packet["binding_sha256"],
    )
    record["binding_sha256"] = packet["binding_sha256"]
    record["authorization_text"] = lifecycle.expected_authorization_text(packet, record)

    verification = lifecycle.verify_authorization_record(
        packet,
        record,
        observed_destination=destination,
        apply_engine_path=REPO_ROOT / "scripts" / "apply_proposal.py",
        production_rollback_artifact=production_rollback_artifact,
        now=datetime(2026, 7, 15, 12, 0, tzinfo=timezone.utc),
    )

    assert verification["checks"]["packet_binding_sha256_self_consistent"] is True
    assert verification["checks"]["approval_gate_ready_at_packet"] is False
    assert verification["safe_to_enter_apply_window"] is False


def test_authorization_packet_uses_repo_relative_dependency_paths_only():
    packet, _record, _destination, _production_rollback_artifact = _authorization_materials()
    encoded = json.dumps(packet, sort_keys=True)

    assert packet["binding"]["engine"]["apply_engine_path"] == "scripts/apply_proposal.py"
    assert packet["binding"]["approval_gate_dependency"]["path"] == "scripts/kb_apply_prereqs.sql"
    assert "/private/tmp" not in encoded
    assert "/tmp" not in encoded
