from __future__ import annotations

import hashlib
import json
import os
import shutil
import subprocess
import sys
import uuid
from pathlib import Path

import pytest

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

import compile_kb_source_packet as compiler  # noqa: E402
import run_leo_integrated_learning_lifecycle as lifecycle  # noqa: E402


def _write_source_packet(tmp_path: Path) -> Path:
    packet = tmp_path / "source-packet"
    prepared = packet / "prepared"
    prepared.mkdir(parents=True)
    artifact = REPO_ROOT / "fixtures" / "working-leo" / "document-ingestion-v1.json"
    manifest = REPO_ROOT / "fixtures" / "working-leo" / "source-compiler-manifest-v1.json"
    extracted = prepared / "extracted-text.txt"
    manifest_target = prepared / "source-manifest.json"
    shutil.copyfile(artifact, extracted)
    shutil.copyfile(manifest, manifest_target)
    expected = compiler.compile_source_packet(extracted, extracted, manifest_target)
    (packet / "proposal-packet.json").write_text(
        json.dumps(expected, indent=2, sort_keys=True) + "\n",
        encoding="utf-8",
    )
    return packet


def _docker_available() -> bool:
    if not shutil.which("docker"):
        return False
    result = subprocess.run(
        ["docker", "info", "--format", "{{.ServerVersion}}"],
        text=True,
        capture_output=True,
        check=False,
    )
    return result.returncode == 0


def _commit_test_repository(path: Path, files: dict[str, str]) -> None:
    subprocess.run(["git", "init", "-q", str(path)], check=True)
    for relative_path, contents in files.items():
        target = path / relative_path
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_text(contents, encoding="utf-8")
    subprocess.run(["git", "-C", str(path), "add", "."], check=True)
    subprocess.run(
        [
            "git",
            "-C",
            str(path),
            "-c",
            "user.name=Lifecycle Test",
            "-c",
            "user.email=lifecycle-test@example.invalid",
            "commit",
            "-qm",
            "test engine",
        ],
        check=True,
    )


def test_source_packet_recompiles_to_retained_expectation(tmp_path: Path) -> None:
    packet = _write_source_packet(tmp_path)

    compiled, expected, paths = lifecycle.compile_source_packet(packet)

    assert compiled == expected
    assert lifecycle.sha256_file(paths["artifact"]) == expected["validated_manifest"]["artifact_sha256"]
    assert compiled["status"] == "pending_review"
    assert compiled["strict_child_proposal"]["status"] == "pending_review"


def test_source_packet_refuses_retained_packet_drift(tmp_path: Path) -> None:
    packet = _write_source_packet(tmp_path)
    expected_path = packet / "proposal-packet.json"
    expected = json.loads(expected_path.read_text(encoding="utf-8"))
    expected["strict_child_proposal"]["status"] = "approved"
    expected_path.write_text(json.dumps(expected), encoding="utf-8")

    with pytest.raises(lifecycle.LifecycleError, match="does not match retained"):
        lifecycle.compile_source_packet(packet)


def test_execution_identity_requires_exact_committed_dependency_bytes(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    repo = tmp_path / "repo"
    repo.mkdir()
    _commit_test_repository(repo, {"scripts/engine.py": "print('committed')\n"})
    engine = repo / "scripts" / "engine.py"
    monkeypatch.setattr(lifecycle, "REPO_ROOT", repo)
    monkeypatch.setattr(lifecycle, "ENGINE_INPUT_PATHS", {"engine.runner": engine})

    identity = lifecycle.execution_identity()
    assert identity["engine_paths_match_head"] is True
    assert identity["engine_relpaths"] == ["scripts/engine.py"]
    assert identity["engine_head_sha256"]["engine.runner"] == hashlib.sha256(engine.read_bytes()).hexdigest()

    engine.write_text("print('dirty')\n", encoding="utf-8")
    with pytest.raises(lifecycle.LifecycleError, match="does not match"):
        lifecycle.execution_identity()


def test_engine_dependency_closure_includes_transitive_imports() -> None:
    assert {
        "engine.proposal_normalizer",
        "engine.rich_proposal_plan",
        "engine.clone_checkpoint",
    } <= lifecycle.ENGINE_INPUT_PATHS.keys()


def test_exact_identities_adds_observed_evidence_ids(tmp_path: Path) -> None:
    packet = _write_source_packet(tmp_path)
    compiled, _expected, _paths = lifecycle.compile_source_packet(packet)
    child = compiled["strict_child_proposal"]
    apply_payload = child["payload"]["apply_payload"]
    planned = apply_payload["evidence"][0]
    evidence_id = "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee"

    identities = lifecycle.exact_identities(
        child,
        {
            "claim_evidence": [{"id": evidence_id, **planned}],
            "claim_edges": [],
        },
    )

    assert identities["proposal_id"] == child["id"]
    assert identities["claim_ids"] == [row["id"] for row in apply_payload["claims"]]
    assert identities["source_ids"] == [row["id"] for row in apply_payload["sources"]]
    assert identities["planned_evidence_keys"] == [
        {
            "claim_id": row["claim_id"],
            "source_id": row["source_id"],
            "role": row["role"],
        }
        for row in apply_payload["evidence"]
    ]
    assert identities["evidence_ids"] == [evidence_id]


def test_lifecycle_requires_explicit_approve_action(tmp_path: Path) -> None:
    with pytest.raises(lifecycle.LifecycleError, match="only supported explicit review action"):
        lifecycle.run_lifecycle(
            tmp_path / "unused",
            tmp_path / "receipt.json",
            review_action="inspect",
        )


def _application_role(*, complete_settings: bool = True) -> dict:
    role = {
        "name": "kb_apply",
        "can_login": True,
        "superuser": False,
        "create_db": False,
        "create_role": False,
        "inherit": False,
        "replication": False,
        "bypass_rls": False,
        "connection_limit": -1,
        "default_transaction_read_only": None,
    }
    if complete_settings:
        role["settings"] = []
        role["database_settings"] = []
    return role


def _minimal_loaded_manifest(*, complete_settings: bool = True) -> dict:
    return {
        "singleton": {
            "identity": {"database": lifecycle.DATABASE, "transaction_read_only": "on"},
            "application_roles": {"items": [_application_role(complete_settings=complete_settings)]},
            "role_memberships": {"items": []},
            "object_acl": {"items": []},
        },
        "tables": {"public.fixture": {"row_count": 0, "rowset_md5": "0" * 32}},
        "performance": {},
    }


def _write_exporter_receipt(
    path: Path,
    dump: Path,
    manifest_path: Path,
    loaded_manifest: dict,
    *,
    source: dict[str, str] | None = None,
) -> dict:
    source = source or {
        "ssh_target": "disposable-test",
        "container": "teleo-test-source",
        "database": lifecycle.DATABASE,
        "service": "disposable-postgres-test",
    }
    snapshot = {
        "captured_at_utc": "2026-07-18T00:00:00+00:00",
        "exported_snapshot_id": "00000003-00000001-1",
        "txid_snapshot": "1:1:",
        "wal_lsn": "0/1",
        "system_identifier": "123456789",
    }
    authorization_ref = "codex-delegation:disposable-genesis-test"
    manifest_sha256 = lifecycle.sha256_file(manifest_path)
    dump_sha256 = lifecycle.sha256_file(dump)
    manifest_sql_sha256 = lifecycle.sha256_file(lifecycle.PARITY_SQL_PATH)
    source_context_sha256 = hashlib.sha256(b"disposable-source-context").hexdigest()
    binding = lifecycle.snapshot_capture.build_provenance_binding(
        run_id="disposable-genesis-test",
        authorization_ref=authorization_ref,
        source=source,
        snapshot=snapshot,
        dump_sha256=dump_sha256,
        manifest_sql_sha256=manifest_sql_sha256,
        source_manifest_sha256=manifest_sha256,
        source_context_sha256=source_context_sha256,
    )
    service_state = {
        "ActiveState": "active",
        "SubState": "running",
        "MainPID": "123",
        "NRestarts": "0",
        "ActiveEnterTimestamp": "2026-07-18T00:00:00+00:00",
    }
    receipt = {
        "artifact": lifecycle.TRUSTED_EXPORTER_ARTIFACT,
        "schema": lifecycle.snapshot_capture.SOURCE_SNAPSHOT_RECEIPT_SCHEMA,
        "receipt_version": 2,
        "status": "pass",
        "run_id": "disposable-genesis-test",
        "capture_authorization_ref": authorization_ref,
        "source": source,
        "snapshot_exported": True,
        "snapshot": snapshot,
        "dump": {"path": str(dump), "bytes": dump.stat().st_size, "sha256": dump_sha256},
        "manifest": {
            "path": str(manifest_path),
            "sha256": manifest_sha256,
            "manifest_sql_sha256": manifest_sql_sha256,
            "table_count": len(loaded_manifest["tables"]),
            "total_rows": sum(int(row["row_count"]) for row in loaded_manifest["tables"].values()),
            "application_roles": loaded_manifest["singleton"]["application_roles"]["items"],
        },
        "source_context": {"path": "source-context.txt", "sha256": source_context_sha256},
        "source_service": {"before": service_state, "after": service_state, "unchanged": True},
        "provenance_binding": binding,
        "service_unchanged": True,
        "production_db_mutated": False,
        "telegram_send_attempted": False,
    }
    path.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    path.chmod(0o600)
    return receipt


def _trust_test_exporter_receipt(
    monkeypatch: pytest.MonkeyPatch,
    path: Path,
    receipt: dict,
) -> None:
    registry = dict(lifecycle.TRUSTED_EXPORTER_RECEIPTS)
    registry[lifecycle.sha256_file(path)] = {
        "run_id": receipt["run_id"],
        "capture_authorization_ref": receipt["capture_authorization_ref"],
        "source": receipt["source"],
        "dump_sha256": receipt["dump"]["sha256"],
        "manifest_sha256": receipt["manifest"]["sha256"],
        "manifest_sql_sha256": receipt["manifest"]["manifest_sql_sha256"],
        "provenance_binding_sha256": receipt["provenance_binding"]["sha256"],
    }
    monkeypatch.setattr(lifecycle, "TRUSTED_EXPORTER_RECEIPTS", registry)


def _genesis_cli(tmp_path: Path) -> list[str]:
    return [
        "--source-packet",
        str(tmp_path / "packet"),
        "--output",
        str(tmp_path / "lifecycle-receipt.json"),
        "--review-action",
        "approve",
        "--genesis-dump",
        str(tmp_path / "genesis.dump"),
        "--genesis-manifest",
        str(tmp_path / "manifest.jsonl"),
        "--genesis-exporter-receipt",
        str(tmp_path / "exporter-receipt.json"),
    ]


def test_genesis_cli_requires_complete_exporter_receipt_set(tmp_path: Path) -> None:
    with pytest.raises(SystemExit):
        lifecycle.parse_args(_genesis_cli(tmp_path)[:-2])


def test_genesis_cli_rejects_caller_only_hash_flags(tmp_path: Path) -> None:
    with pytest.raises(SystemExit):
        lifecycle.parse_args([*_genesis_cli(tmp_path), "--genesis-dump-sha256", "a" * 64])


def test_genesis_cli_rejects_moving_image_tag(tmp_path: Path) -> None:
    with pytest.raises(SystemExit):
        lifecycle.parse_args([*_genesis_cli(tmp_path), "--image", "postgres:16-alpine"])


def test_exporter_receipt_dump_drift_fails_before_docker(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    dump = tmp_path / "genesis.dump"
    manifest = tmp_path / "manifest.jsonl"
    exporter_receipt = tmp_path / "exporter-receipt.json"
    dump.write_bytes(b"PGDMP-real-enough-for-signature-validation")
    manifest.write_text("{}\n", encoding="utf-8")
    loaded_manifest = _minimal_loaded_manifest()
    receipt = _write_exporter_receipt(exporter_receipt, dump, manifest, loaded_manifest)
    _trust_test_exporter_receipt(monkeypatch, exporter_receipt, receipt)
    dump.write_bytes(dump.read_bytes() + b"drift")
    monkeypatch.setattr(lifecycle, "load_manifest", lambda _path: loaded_manifest)

    with pytest.raises(lifecycle.LifecycleError, match="dump_hash_from_exporter_receipt"):
        lifecycle.validate_genesis_inputs(
            dump,
            manifest,
            exporter_receipt,
            lifecycle.DEFAULT_GENESIS_IMAGE,
        )


def test_exporter_receipt_rebound_source_identity_fails_closed(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    dump = tmp_path / "genesis.dump"
    manifest = tmp_path / "manifest.jsonl"
    exporter_receipt = tmp_path / "exporter-receipt.json"
    dump.write_bytes(b"PGDMP-real-enough-for-signature-validation")
    manifest.write_text("{}\n", encoding="utf-8")
    loaded_manifest = _minimal_loaded_manifest()
    receipt = _write_exporter_receipt(exporter_receipt, dump, manifest, loaded_manifest)
    _trust_test_exporter_receipt(monkeypatch, exporter_receipt, receipt)
    receipt["source"]["database"] = "attacker_rebound"
    receipt["provenance_binding"] = lifecycle.snapshot_capture.build_provenance_binding(
        run_id=receipt["run_id"],
        authorization_ref=receipt["capture_authorization_ref"],
        source=receipt["source"],
        snapshot=receipt["snapshot"],
        dump_sha256=receipt["dump"]["sha256"],
        manifest_sql_sha256=receipt["manifest"]["manifest_sql_sha256"],
        source_manifest_sha256=receipt["manifest"]["sha256"],
        source_context_sha256=receipt["source_context"]["sha256"],
    )
    exporter_receipt.write_text(json.dumps(receipt), encoding="utf-8")
    exporter_receipt.chmod(0o600)
    monkeypatch.setattr(lifecycle, "load_manifest", lambda _path: loaded_manifest)

    with pytest.raises(lifecycle.LifecycleError, match="trusted_exporter_receipt_hash"):
        lifecycle.validate_genesis_inputs(
            dump,
            manifest,
            exporter_receipt,
            lifecycle.DEFAULT_GENESIS_IMAGE,
        )


def test_legacy_manifest_role_settings_projection_is_narrow_and_nonmutating() -> None:
    manifest = {
        "singleton": {
            "application_roles": {
                "items": [
                    {
                        "name": "kb_observatory_read",
                        "default_transaction_read_only": "on",
                    },
                    {
                        "name": "kb_apply",
                        "default_transaction_read_only": None,
                    },
                ]
            }
        }
    }

    projected = lifecycle.manifest_for_role_replay(manifest)

    assert "settings" not in manifest["singleton"]["application_roles"]["items"][0]
    assert projected["singleton"]["application_roles"]["items"][0]["settings"] == [
        {"name": "default_transaction_read_only", "value": "on"}
    ]
    assert projected["singleton"]["application_roles"]["items"][0]["database_settings"] == []
    assert projected["singleton"]["application_roles"]["items"][1]["settings"] == []
    assert lifecycle.role_settings_capture_complete(manifest) is False


def test_private_staging_is_read_only_and_original_path_drift_is_not_consumed(tmp_path: Path) -> None:
    packet = _write_source_packet(tmp_path)
    dump = tmp_path / "genesis.dump"
    manifest = tmp_path / "manifest.jsonl"
    exporter_receipt = tmp_path / "exporter-receipt.json"
    dump.write_bytes(b"PGDMP-staged-input")
    manifest.write_text("{}\n", encoding="utf-8")
    exporter_receipt.write_text("{}\n", encoding="utf-8")
    exporter_receipt.chmod(0o600)

    staged = lifecycle.stage_lifecycle_inputs(packet, dump, manifest, exporter_receipt)
    try:
        before = lifecycle.verify_staged_inputs(staged)
        dump.write_bytes(b"PGDMP-mutated-original")
        (packet / "proposal-packet.json").write_text("{}\n", encoding="utf-8")
        after = lifecycle.verify_staged_inputs(staged)

        assert before == after
        assert staged.genesis_dump.read_bytes() == b"PGDMP-staged-input"
        assert staged.root.stat().st_mode & 0o777 == 0o500
        assert all(
            (staged.root / row["relative_path"]).stat().st_mode & 0o777 == 0o400 for row in staged.files.values()
        )
    finally:
        cleanup = lifecycle.cleanup_staged_inputs(staged.root)

    assert cleanup["root_absent"] is True


def test_private_staging_detects_consumed_byte_mutation(tmp_path: Path) -> None:
    packet = _write_source_packet(tmp_path)
    dump = tmp_path / "genesis.dump"
    manifest = tmp_path / "manifest.jsonl"
    exporter_receipt = tmp_path / "exporter-receipt.json"
    dump.write_bytes(b"PGDMP-staged-input")
    manifest.write_text("{}\n", encoding="utf-8")
    exporter_receipt.write_text("{}\n", encoding="utf-8")
    exporter_receipt.chmod(0o600)
    staged = lifecycle.stage_lifecycle_inputs(packet, dump, manifest, exporter_receipt)
    staged_dump = staged.genesis_dump
    try:
        staged_dump.chmod(0o600)
        staged_dump.write_bytes(b"PGDMP-mutated-staging")
        staged_dump.chmod(0o400)
        with pytest.raises(lifecycle.LifecycleError, match="staging bytes drifted"):
            lifecycle.verify_staged_inputs(staged)
    finally:
        cleanup = lifecycle.cleanup_staged_inputs(staged.root)

    assert cleanup["root_absent"] is True


def test_genesis_initialization_cannot_call_synthetic_bootstrap(tmp_path: Path) -> None:
    class RestoreOnlyPostgres:
        def __init__(self) -> None:
            self.restored = False

        def restore_genesis(self, _inputs):
            self.restored = True
            return {"parity": {"status": "pass"}}

        def psql(self, sql: str) -> str:
            assert sql.startswith("alter role kb_review password")
            return ""

    postgres = RestoreOnlyPostgres()

    result = lifecycle.initialize_database(
        postgres,
        genesis=object(),
        review_password="unused",
        apply_password="unused",
    )

    assert postgres.restored is True
    assert result["mode"] == "exporter_receipt_bound_genesis_restore"
    assert result["synthetic_schema_bootstrap_skipped"] is True
    assert result["canonical_payload_preseed_performed"] is False


def test_fresh_initial_state_rejects_proposal_and_payload_preseed() -> None:
    empty_rows = {family: [] for family in lifecycle.apply_lifecycle.ROW_TABLES}
    with pytest.raises(lifecycle.LifecycleError, match="proposal or approval preseed"):
        lifecycle.require_fresh_initial_state(
            {"state": {"proposal": {"id": "preseed"}, "immutable_approval": None, "canonical_rows": empty_rows}}
        )

    preseed_rows = {family: [] for family in lifecycle.apply_lifecycle.ROW_TABLES}
    preseed_rows["claims"] = [{"id": "preseed"}]
    with pytest.raises(lifecycle.LifecycleError, match="canonical payload target preseed"):
        lifecycle.require_fresh_initial_state(
            {"state": {"proposal": None, "immutable_approval": None, "canonical_rows": preseed_rows}}
        )


def test_payload_controlled_fingerprint_ignores_generated_fields_but_detects_payload_drift() -> None:
    rows = {family: [] for family in lifecycle.PAYLOAD_CONTROLLED_FIELDS}
    rows["claims"] = [
        {
            "id": "11111111-1111-4111-8111-111111111111",
            "type": "fact",
            "text": "Stable payload text",
            "status": "open",
            "confidence": 0.7,
            "tags": ["test"],
            "created_by": None,
            "superseded_by": None,
            "created_at": "2026-01-01T00:00:00+00:00",
        }
    ]
    first = lifecycle.payload_controlled_fingerprint(rows)
    rows["claims"][0]["created_at"] = "2027-01-01T00:00:00+00:00"
    rows["claims"][0]["updated_at"] = "2027-01-01T00:00:01+00:00"
    second = lifecycle.payload_controlled_fingerprint(rows)
    rows["claims"][0]["text"] = "Drifted payload text"
    drifted = lifecycle.payload_controlled_fingerprint(rows)

    assert first["sha256"] == second["sha256"]
    assert drifted["sha256"] != first["sha256"]


def _fake_genesis_run(run_number: int) -> dict:
    controlled = {
        "schema": "livingip.leoPayloadControlledRowsFingerprint.v1",
        "sha256": "c" * 64,
        "table_sha256": {family: "d" * 64 for family in lifecycle.PAYLOAD_CONTROLLED_FIELDS},
        "row_counts": {family: 0 for family in lifecycle.PAYLOAD_CONTROLLED_FIELDS},
        "rows": {family: [] for family in lifecycle.PAYLOAD_CONTROLLED_FIELDS},
    }
    return {
        "status": "pass",
        "run_number": run_number,
        "source": {
            "artifact_sha256": "1" * 64,
            "manifest_canonical_sha256": "2" * 64,
            "retained_proposal_packet_file_sha256": "3" * 64,
            "retained_proposal_packet_canonical_sha256": "4" * 64,
        },
        "compiled": {"bundle_sha256": "4" * 64},
        "identities": {
            "proposal_id": "11111111-1111-4111-8111-111111111111",
            "claim_ids": [],
            "source_ids": [],
            "planned_evidence_keys": [],
            "evidence_ids": [f"generated-{run_number}"],
            "edge_ids": [],
        },
        "phases": {
            "staged_pending_review": {"proposal": {"status": "pending_review"}},
            "approved_unapplied": {"proposal": {"status": "approved"}},
            "applied": {
                "proposal": {"status": "applied", "applied_at": f"2026-01-0{run_number}T00:00:00+00:00"},
                "payload_controlled_fingerprint": controlled,
                "worker_action": {
                    "exact_candidate_id": "11111111-1111-4111-8111-111111111111",
                    "credential_role": "kb_apply",
                    "retry_failure_count": 1,
                    "failure_state_after_success": {},
                    "private_receipt_mode": "0600",
                },
                "apply_receipt": {
                    "hashes": {"apply_payload_sha256": "5" * 64},
                    "expected_row_counts": controlled["row_counts"],
                    "actual_row_counts": controlled["row_counts"],
                },
            },
            "recompiled_readback": {
                "bundle_sha256": "4" * 64,
                "matches_initial_compile": True,
            },
        },
        "database_initialization": {
            "genesis": {
                "dump": {"sha256": "6" * 64},
                "manifest": {"sha256": "7" * 64},
                "exporter_receipt": {
                    "sha256": "8" * 64,
                    "provenance_binding_sha256": "9" * 64,
                },
                "image": {"reference": lifecycle.DEFAULT_GENESIS_IMAGE},
                "parity": {
                    "status": "pass",
                    "problems": [],
                    "claim_scope": "full_captured_manifest_including_role_and_database_settings",
                    "role_global_parity": {
                        "status": "proven",
                        "captured_source_settings_present": True,
                        "reason": None,
                    },
                },
                "key_counts": {"public.claims": 10},
                "canonical_schema_table_count": 10,
            }
        },
        "cleanup": {"all": True},
    }


def _mocked_genesis_case(tmp_path: Path) -> tuple[Path, Path, Path, Path]:
    packet = _write_source_packet(tmp_path)
    dump = tmp_path / "genesis.dump"
    manifest = tmp_path / "genesis-manifest.jsonl"
    exporter_receipt = tmp_path / "exporter-receipt.json"
    dump.write_bytes(b"PGDMPx")
    manifest.write_text("{}\n", encoding="utf-8")
    _write_exporter_receipt(exporter_receipt, dump, manifest, _minimal_loaded_manifest())
    return packet, dump, manifest, exporter_receipt


def _fake_validate_genesis(
    dump: Path,
    manifest: Path,
    exporter_receipt: Path,
    image: str,
    **paths,
) -> lifecycle.GenesisInputs:
    payload = json.loads(exporter_receipt.read_text(encoding="utf-8"))
    return lifecycle.GenesisInputs(
        dump=dump,
        dump_sha256=lifecycle.sha256_file(dump),
        dump_bytes=dump.stat().st_size,
        manifest_path=manifest,
        manifest_sha256=lifecycle.sha256_file(manifest),
        manifest_bytes=manifest.stat().st_size,
        manifest=_minimal_loaded_manifest(),
        image=image,
        exporter_receipt_path=exporter_receipt,
        exporter_receipt_sha256=lifecycle.sha256_file(exporter_receipt),
        exporter_receipt=payload,
        source_identity=payload["source"],
        capture_authorization_ref=payload["capture_authorization_ref"],
        provenance_binding_sha256=payload["provenance_binding"]["sha256"],
        trust_root_receipt_sha256=lifecycle.sha256_file(exporter_receipt),
        role_settings_capture_complete=True,
        parity_sql_path=paths["parity_sql_path"],
        guard_prerequisites_path=paths["guard_prerequisites_path"],
        approve_script_path=paths["approve_script_path"],
        apply_worker_script_path=paths["apply_worker_script_path"],
        apply_script_path=paths["apply_script_path"],
    )


def test_genesis_lifecycle_repeats_semantically_and_writes_private_receipt(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    packet, dump, manifest, exporter_receipt = _mocked_genesis_case(tmp_path)
    calls: list[int] = []

    monkeypatch.setattr(lifecycle, "validate_genesis_inputs", _fake_validate_genesis)

    def fake_run(*_args, run_number: int, **_kwargs):
        calls.append(run_number)
        return _fake_genesis_run(run_number)

    monkeypatch.setattr(lifecycle, "run_lifecycle", fake_run)
    monkeypatch.setattr(
        lifecycle,
        "verify_run_group_cleanup",
        lambda run_group: {"checked": True, "run_group": run_group, "container_names": [], "all_absent": True},
    )
    output = tmp_path / "genesis-lifecycle-receipt.json"

    receipt = lifecycle.run_genesis_lifecycle(
        packet,
        output,
        review_action="approve",
        genesis_dump=dump,
        genesis_manifest=manifest,
        genesis_exporter_receipt=exporter_receipt,
        image=lifecycle.DEFAULT_GENESIS_IMAGE,
    )

    assert calls == [1, 2]
    assert receipt["status"] == "pass"
    assert receipt["repeatability"]["semantic_match"] is True
    assert len(set(receipt["repeatability"]["semantic_fingerprints_sha256"])) == 1
    assert receipt["repeatability"]["identical_post_apply_fingerprints"] is True
    assert receipt["checks"]["both_runs_consumed_preflight_bound_bytes"] is True
    assert receipt["cleanup"]["zero_staging_leftovers"] is True
    assert receipt["cleanup"]["zero_orphan_containers"] is True
    assert output.stat().st_mode & 0o777 == 0o600
    assert json.loads(output.read_text(encoding="utf-8")) == receipt


def test_genesis_lifecycle_fails_closed_on_semantic_repeatability_drift(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    packet, dump, manifest, exporter_receipt = _mocked_genesis_case(tmp_path)
    monkeypatch.setattr(lifecycle, "validate_genesis_inputs", _fake_validate_genesis)

    def drifted_run(*_args, run_number: int, **_kwargs):
        run = _fake_genesis_run(run_number)
        if run_number == 2:
            run["phases"]["applied"]["payload_controlled_fingerprint"]["sha256"] = "e" * 64
        return run

    monkeypatch.setattr(lifecycle, "run_lifecycle", drifted_run)
    monkeypatch.setattr(
        lifecycle,
        "verify_run_group_cleanup",
        lambda run_group: {"checked": True, "run_group": run_group, "container_names": [], "all_absent": True},
    )

    receipt = lifecycle.run_genesis_lifecycle(
        packet,
        tmp_path / "repeatability-drift-receipt.json",
        review_action="approve",
        genesis_dump=dump,
        genesis_manifest=manifest,
        genesis_exporter_receipt=exporter_receipt,
        image=lifecycle.DEFAULT_GENESIS_IMAGE,
    )

    assert receipt["status"] == "fail"
    assert receipt["checks"]["both_runs_passed"] is True
    assert receipt["checks"]["both_runs_match_semantically"] is False
    assert receipt["checks"]["identical_post_apply_fingerprints"] is False
    assert len(set(receipt["repeatability"]["semantic_fingerprints_sha256"])) == 2


def test_genesis_lifecycle_fails_closed_on_orphan_cleanup(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    packet, dump, manifest, exporter_receipt = _mocked_genesis_case(tmp_path)
    monkeypatch.setattr(lifecycle, "validate_genesis_inputs", _fake_validate_genesis)
    monkeypatch.setattr(
        lifecycle,
        "run_lifecycle",
        lambda *_args, run_number, **_kwargs: _fake_genesis_run(run_number),
    )
    monkeypatch.setattr(
        lifecycle,
        "verify_run_group_cleanup",
        lambda run_group: {
            "checked": True,
            "run_group": run_group,
            "container_names": ["orphaned-container"],
            "all_absent": False,
        },
    )

    receipt = lifecycle.run_genesis_lifecycle(
        packet,
        tmp_path / "failed-cleanup-receipt.json",
        review_action="approve",
        genesis_dump=dump,
        genesis_manifest=manifest,
        genesis_exporter_receipt=exporter_receipt,
        image=lifecycle.DEFAULT_GENESIS_IMAGE,
    )

    assert receipt["status"] == "fail"
    assert receipt["checks"]["zero_orphan_containers"] is False


@pytest.mark.skipif(not _docker_available(), reason="Docker daemon is required for the isolated lifecycle")
def test_disposable_postgres_lifecycle_end_to_end(tmp_path: Path) -> None:
    packet = _write_source_packet(tmp_path)
    output = tmp_path / "integrated-learning-receipt.json"

    receipt = lifecycle.run_lifecycle(
        packet,
        output,
        review_action="approve",
        review_note="Approved exact fixture payload for isolated lifecycle regression coverage.",
    )

    assert receipt["status"] == "pass", receipt.get("error")
    assert receipt["current_tier"] == "realistic_isolated_local_container_end_to_end"
    assert receipt["phases"]["staged_pending_review"]["proposal"]["status"] == "pending_review"
    assert receipt["phases"]["approved_unapplied"]["proposal"]["status"] == "approved"
    assert receipt["phases"]["approved_unapplied"]["proposal"]["applied_at"] is None
    assert receipt["phases"]["applied"]["proposal"]["status"] == "applied"
    worker = receipt["phases"]["applied"]["worker_action"]
    assert worker["report_only"]["expected_marker_present"] is True
    assert worker["expected_retry_failure"]["returncode"] == 1
    assert worker["retry_failure_count"] == 1
    assert worker["failure_state_after_success"] == {}
    assert worker["enabled_retry"]["expected_marker_present"] is True
    assert worker["post_apply_exact_refusal"]["returncode"] == 1
    assert worker["post_apply_exact_refusal"]["expected_refusal_marker_present"] is True
    assert worker["private_receipt_mode"] == "0600"
    assert receipt["phases"]["applied"]["apply_receipt"]["replay_ready"] is True
    assert receipt["phases"]["recompiled_readback"]["matches_initial_compile"] is True
    assert receipt["identities"]["evidence_ids"]
    assert len(receipt["checks"]) >= 20
    assert all(receipt["checks"].values())
    assert receipt["cleanup"]["all"] is True
    assert output.stat().st_mode & 0o777 == 0o600
    assert json.loads(output.read_text(encoding="utf-8")) == receipt


def _build_real_docker_genesis(tmp_path: Path) -> tuple[Path, Path, Path]:
    run_id = uuid.uuid4().hex[:10]
    source = lifecycle.DisposablePostgres(
        f"leo-real-genesis-source-{os.getpid()}-{run_id}",
        "disposable-admin-password",
        image=lifecycle.DEFAULT_GENESIS_IMAGE,
        genesis_backed=True,
        run_group=f"leo-real-genesis-source-{run_id}",
    )
    dump = tmp_path / "real-genesis.dump"
    manifest_path = tmp_path / "real-genesis-manifest.jsonl"
    exporter_receipt = tmp_path / "real-genesis-exporter-receipt.json"
    try:
        source.start()
        source.psql(lifecycle.bootstrap_sql("review-password", "apply-password"))
        source.psql(
            "alter role kb_apply set statement_timeout to '17s';\n"
            "alter role kb_review in database teleo set lock_timeout to '19s';"
        )
        manifest_output = source.psql(lifecycle.PARITY_SQL_PATH.read_text(encoding="utf-8"))
        manifest_path.write_text(manifest_output + "\n", encoding="utf-8")
        manifest_path.chmod(0o600)
        container_dump = "/tmp/leo-real-genesis.dump"
        source._run(
            [
                "exec",
                source.name,
                "pg_dump",
                "-U",
                "postgres",
                "-d",
                lifecycle.DATABASE,
                "--format=custom",
                "--compress=9",
                "--no-owner",
                f"--file={container_dump}",
            ]
        )
        source._run(["cp", f"{source.name}:{container_dump}", str(dump)])
        dump.chmod(0o600)
        loaded_manifest = lifecycle.load_manifest(manifest_path)
        roles = {role["name"]: role for role in loaded_manifest["singleton"]["application_roles"]["items"]}
        assert roles["kb_apply"]["settings"] == [{"name": "statement_timeout", "value": "17s"}]
        assert roles["kb_review"]["database_settings"] == [{"name": "lock_timeout", "value": "19s"}]
        _write_exporter_receipt(
            exporter_receipt,
            dump,
            manifest_path,
            loaded_manifest,
            source={
                "ssh_target": "disposable-docker-test",
                "container": source.name,
                "database": lifecycle.DATABASE,
                "service": "disposable-postgres-test",
            },
        )
    finally:
        cleanup = source.cleanup()
        assert all(
            (
                cleanup["container_absent"],
                cleanup["name_readback_clear"],
                cleanup["instance_label_readback_clear"],
                cleanup["run_group_readback_clear"],
                cleanup["network_mode_was_none"],
                cleanup["tmpfs_data_dir_was_present"],
                cleanup["docker_volume_mounts_observed"] == [],
            )
        )
    return dump, manifest_path, exporter_receipt


@pytest.mark.skipif(not _docker_available(), reason="Docker daemon is required for the real genesis lifecycle")
def test_real_docker_genesis_restore_worker_parity_and_repeatability(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    packet = _write_source_packet(tmp_path)
    dump, manifest, exporter_receipt = _build_real_docker_genesis(tmp_path)
    receipt_payload = json.loads(exporter_receipt.read_text(encoding="utf-8"))
    _trust_test_exporter_receipt(monkeypatch, exporter_receipt, receipt_payload)
    output = tmp_path / "real-genesis-lifecycle-receipt.json"

    receipt = lifecycle.run_genesis_lifecycle(
        packet,
        output,
        review_action="approve",
        review_note="Approved exact Docker genesis payload for regression coverage.",
        genesis_dump=dump,
        genesis_manifest=manifest,
        genesis_exporter_receipt=exporter_receipt,
        image=lifecycle.DEFAULT_GENESIS_IMAGE,
    )

    assert receipt["status"] == "pass", receipt.get("error")
    assert receipt["execution"]["git_head"]
    assert receipt["execution"]["engine_set_sha256"]
    assert receipt["execution"]["engine_paths_match_head"] is True
    assert receipt["execution"]["engine_sha256"] == receipt["execution"]["engine_head_sha256"]
    assert receipt["pre_docker_validation"]["trusted_exporter_receipt_validated"] is True
    assert receipt["pre_docker_validation"]["committed_engine_dependency_closure_verified"] is True
    assert receipt["checks"]["both_runs_consumed_preflight_bound_bytes"] is True
    assert receipt["checks"]["committed_engine_dependency_closure_verified"] is True
    assert receipt["checks"]["portable_receipt_has_no_absolute_paths"] is True
    assert lifecycle.absolute_path_value_pointers(receipt) == []
    assert receipt["repeatability"]["identical_post_apply_fingerprints"] is True
    assert len(set(receipt["repeatability"]["post_apply_fingerprints_sha256"])) == 1
    assert receipt["cleanup"]["zero_orphan_containers"] is True
    assert receipt["cleanup"]["zero_staging_leftovers"] is True
    for run in receipt["runs"]:
        assert run["status"] == "pass", run.get("error")
        assert (
            run["consumed_inputs"]["preflight_manifest_sha256"]
            == receipt["immutable_staging"]["preflight_manifest_sha256"]
        )
        assert run["consumed_inputs"]["executing_git_head"] == receipt["execution"]["git_head"]
        assert run["consumed_inputs"]["engine_set_sha256"] == receipt["execution"]["engine_set_sha256"]
        assert run["database_initialization"]["genesis"]["parity"]["role_global_parity"]["status"] == "proven"
        assert run["phases"]["applied"]["worker_action"]["retry_failure_count"] == 1
        assert run["phases"]["applied"]["worker_action"]["private_receipt_mode"] == "0600"
    assert output.stat().st_mode & 0o777 == 0o600
    assert json.loads(output.read_text(encoding="utf-8")) == receipt
