#!/usr/bin/env python3
"""Verify one bound source-to-GCP restore, reasoning, and cleanup lifecycle."""

from __future__ import annotations

import argparse
import hashlib
import ipaddress
import json
import re
from datetime import UTC, datetime
from itertools import pairwise
from pathlib import Path
from typing import Any

try:
    from .private_receipt_io import print_private_receipt_summary
    from .restore_gcp_generated_postgres_snapshot import DEFAULT_RESTORE_SERVICE_ACCOUNT
    from .verify_postgres_parity_manifest import REVIEWED_MANIFEST_SQL_SHA256
    from .verify_vps_canonical_snapshot_delta import DELTA_SCHEMA, parse_timestamp, write_private_json
except ImportError:  # pragma: no cover - direct script execution
    from private_receipt_io import print_private_receipt_summary
    from restore_gcp_generated_postgres_snapshot import DEFAULT_RESTORE_SERVICE_ACCOUNT
    from verify_postgres_parity_manifest import REVIEWED_MANIFEST_SQL_SHA256
    from verify_vps_canonical_snapshot_delta import DELTA_SCHEMA, parse_timestamp, write_private_json

SOURCE_SCHEMA = "livingip.sourceSnapshotReceipt.v2"
BLIND_REASONING_SCHEMA = "livingip.gcpGeneratedDbBlindClaimCanary.v1"
REASONING_COMPUTE_SCHEMA = "livingip.gcpReasoningComputeAttestation.v1"
RETRIEVAL_RECEIPT_SCHEMA = "livingip.teleoKbRetrievalReceipt.v1"
UUID_RE = re.compile(r"\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b", re.I)
SHA256_RE = re.compile(r"[0-9a-f]{64}\Z")
FORBIDDEN_HANDLE_RE = re.compile(r"\b(?:cory|m3ta)\b", re.I)
EXPECTED_BLIND_ROW_IDS = {
    "2a7ae257-d01d-46f4-b813-63f81bb9c7c7",
    "261c3532-fa32-47d8-a5b5-6cc45035c267",
    "15740795-ecc6-40fa-9a01-3d6bc7c54f79",
}


def sha256_file(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for chunk in iter(lambda: handle.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def load_receipt(path: Path, label: str) -> dict[str, Any]:
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        raise ValueError(f"{label} is unreadable or invalid JSON") from exc
    if not isinstance(payload, dict):
        raise ValueError(f"{label} must be a JSON object")
    return payload


def is_rfc1918(value: Any) -> bool:
    try:
        address = ipaddress.ip_address(str(value))
    except ValueError:
        return False
    return any(
        address in network
        for network in (
            ipaddress.ip_network("10.0.0.0/8"),
            ipaddress.ip_network("172.16.0.0/12"),
            ipaddress.ip_network("192.168.0.0/16"),
        )
    )


def nonempty_all_true(value: Any) -> bool:
    return isinstance(value, dict) and bool(value) and all(item is True for item in value.values())


def required_true_checks(value: Any, required: set[str]) -> bool:
    return nonempty_all_true(value) and required <= set(value)


def service_healthy(value: Any) -> bool:
    if not isinstance(value, dict):
        return False
    try:
        main_pid = int(value.get("MainPID") or 0)
        restarts = int(value.get("NRestarts") or 0)
    except (TypeError, ValueError):
        return False
    return (
        value.get("ActiveState") == "active" and value.get("SubState") == "running" and main_pid > 0 and restarts >= 0
    )


def parity_evidence(details: dict[str, Any]) -> dict[str, bool]:
    structural = details.get("structural_hashes") or {}
    required_structures = {
        "schemas",
        "columns",
        "constraints",
        "indexes",
        "sequences",
        "views",
        "functions",
        "triggers",
        "types",
        "policies",
        "role_memberships",
        "object_ownership",
        "object_acl",
    }
    structural_valid = required_structures <= set(structural) and all(
        bool(SHA256_RE.fullmatch(str((structural.get(kind) or {}).get("source") or "")))
        and (structural.get(kind) or {}).get("source") == (structural.get(kind) or {}).get("target")
        for kind in required_structures
    )
    performance = details.get("performance") or []
    performance_valid = bool(performance) and all(
        bool(row.get("query"))
        and isinstance(row.get("source_ms"), (int, float))
        and isinstance(row.get("target_ms"), (int, float))
        and isinstance(row.get("source_ratio"), (int, float))
        for row in performance
    )
    source_table_count = details.get("source_table_count")
    target_table_count = details.get("target_table_count")
    source_total_rows = details.get("source_total_rows")
    target_total_rows = details.get("target_total_rows")
    return {
        "table_counts": isinstance(source_table_count, int)
        and source_table_count > 0
        and source_table_count == target_table_count,
        "row_counts": isinstance(source_total_rows, int)
        and source_total_rows > 0
        and source_total_rows == target_total_rows,
        "table_hashes": details.get("table_mismatches") == [] and details.get("table_hash_problems") == [],
        "structural_hashes": structural_valid,
        "roles": details.get("application_role_mismatches") == {}
        and details.get("target_extra_application_roles") == [],
        "extensions": details.get("required_extension_mismatches") == {}
        and details.get("target_extra_extensions") == [],
        "performance": performance_valid and details.get("performance_problems") == [],
    }


def blind_reasoning_evidence(reasoning: dict[str, Any], target_db: Any) -> dict[str, bool]:
    result = reasoning.get("result") or {}
    gateway = result.get("gateway") or {}
    child = gateway.get("child_process") or {}
    tool_surface = gateway.get("tool_surface") or {}
    tool_trace = result.get("database_tool_trace") or {}
    calls = tool_trace.get("calls") or []
    wrapper = reasoning.get("wrapper_tool_proof") or {}
    invocations = wrapper.get("invocations") or []
    prompt = str(reasoning.get("prompt") or "")
    reply = str(result.get("reply") or "")
    outcomes = reasoning.get("outcomes") or {}
    required_outcomes = (
        "asks_for_user_iteration_before_live",
        "challenges_weak_support",
        "decomposes_the_bundled_legal_claim",
        "does_not_claim_a_database_change",
        "proposes_candidate_claims",
        "uses_only_m3taversal_handle",
    )
    required_subcommands = {"show", "evidence"}
    discovery_subcommands = {"search", "context"}
    traced_subcommands = {
        str(invocation.get("subcommand")) for call in calls for invocation in (call.get("database_invocations") or [])
    }
    wrapper_subcommands = {str(invocation.get("subcommand")) for invocation in invocations}
    retrieved_row_ids = {str(row_id) for call in calls for row_id in ((call.get("result") or {}).get("row_ids") or [])}
    call_row_ids_valid = bool(calls) and all(
        isinstance((call.get("result") or {}).get("row_ids"), list)
        and isinstance((call.get("result") or {}).get("row_id_count"), int)
        and (call.get("result") or {}).get("row_id_count") >= 0
        and (call.get("result") or {}).get("row_id_count") >= len((call.get("result") or {}).get("row_ids") or [])
        for call in calls
    )
    call_receipts_valid = bool(calls) and all(
        (call.get("result") or {}).get("nonempty") is True
        and (call.get("result") or {}).get("error_detected") is False
        and ((call.get("result") or {}).get("retrieval_receipt") or {}).get("schema") == RETRIEVAL_RECEIPT_SCHEMA
        and bool(
            SHA256_RE.fullmatch(
                str(((call.get("result") or {}).get("retrieval_receipt") or {}).get("semantic_context_sha256") or "")
            )
        )
        and bool(
            SHA256_RE.fullmatch(
                str(((call.get("result") or {}).get("retrieval_receipt") or {}).get("artifact_state_sha256") or "")
            )
        )
        and str(
            ((call.get("result") or {}).get("retrieval_receipt") or {}).get("read_consistency_status") or ""
        ).startswith("stable")
        for call in calls
    )
    wrapper_invocations_valid = bool(invocations) and all(
        invocation.get("database") == target_db
        and invocation.get("returncode") == 0
        and ((invocation.get("database_identity") or {}).get("transaction_read_only") == "on")
        and ((invocation.get("database_identity") or {}).get("default_transaction_read_only") == "on")
        and ((invocation.get("database_identity") or {}).get("ssl") is True)
        and ((invocation.get("database_identity") or {}).get("sslmode") == "verify-full")
        and ((invocation.get("database_identity") or {}).get("server_identity_verified") is True)
        and bool((invocation.get("database_identity") or {}).get("ssl_server_name"))
        and bool(
            SHA256_RE.fullmatch(str((invocation.get("database_identity") or {}).get("ssl_root_cert_sha256") or ""))
        )
        and is_rfc1918((invocation.get("database_identity") or {}).get("server_address"))
        for invocation in invocations
    )
    return {
        "mode": reasoning.get("mode") == "gcp_generated_db_gatewayrunner_blind_claim_no_send",
        "timestamps": bool(reasoning.get("generated_at_utc")) and bool(reasoning.get("completed_at_utc")),
        "source_compute": bool(reasoning.get("source_compute")),
        "blind_prompt": bool(prompt) and UUID_RE.search(prompt) is None,
        "reply_nonempty": bool(reply.strip()),
        "reply_uses_only_m3taversal_handle": FORBIDDEN_HANDLE_RE.search(reply) is None,
        "outcomes": all(outcomes.get(name) is True for name in required_outcomes),
        "gateway": gateway.get("authorized") is True
        and gateway.get("handler_invoked") is True
        and gateway.get("model_free_fallback_used") is False
        and gateway.get("posted_to_telegram") is False,
        "gateway_cleanup": child.get("exitcode") == 0
        and child.get("alive_after_readback") is False
        and child.get("process_group_alive_after_readback") is False
        and child.get("timed_out") is False,
        "send_tool_absent": tool_surface.get("send_message_tool_enabled") is False,
        "tool_trace": tool_trace.get("schema") == "livingip.leoKbToolTrace.v1"
        and tool_trace.get("database_tool_call_proven") is True
        and tool_trace.get("database_retrieval_receipt_proven") is True
        and tool_trace.get("database_tool_calls_read_only") is True
        and tool_trace.get("database_tool_call_count") == len(calls)
        and tool_trace.get("database_tool_completed_count") == len(calls),
        "tool_receipts": call_receipts_valid,
        "expected_claim_and_sources_retrieved": call_row_ids_valid and retrieved_row_ids >= EXPECTED_BLIND_ROW_IDS,
        "required_tool_sequence": required_subcommands <= traced_subcommands
        and bool(discovery_subcommands & traced_subcommands),
        "wrapper": wrapper.get("all_bound_to_supplied_target") is True
        and wrapper.get("all_completed_successfully") is True
        and wrapper.get("complete_start_end_pairing") is True
        and wrapper.get("database_read_only_required") is True
        and wrapper.get("default_read_only_required") is True,
        "wrapper_invocations": wrapper_invocations_valid
        and required_subcommands <= wrapper_subcommands
        and bool(discovery_subcommands & wrapper_subcommands),
        "canonical_status_unchanged": bool(reasoning.get("canonical_status_before"))
        and reasoning.get("canonical_status_before") == reasoning.get("canonical_status_after"),
        "database_identity_unchanged": bool(reasoning.get("database_identity_before"))
        and reasoning.get("database_identity_before") == reasoning.get("database_identity_after"),
        "service_unchanged": service_healthy(reasoning.get("service_before"))
        and reasoning.get("service_before") == reasoning.get("service_after"),
        "temporary_profile_removed": reasoning.get("temp_profile_absent") is True
        and reasoning.get("temp_profile_removed") is True,
    }


def verify_lifecycle(
    *,
    source_path: Path,
    current_source_path: Path,
    source_delta_path: Path,
    restore_path: Path,
    parity_path: Path,
    reasoning_path: Path,
    reasoning_compute_path: Path,
    cleanup_path: Path,
    max_postflight_age_seconds: float = 900.0,
    max_lifecycle_age_seconds: float = 3600.0,
    now: datetime | None = None,
) -> dict[str, Any]:
    if max_postflight_age_seconds <= 0:
        raise ValueError("max postflight age must be positive")
    if max_lifecycle_age_seconds <= 0:
        raise ValueError("max lifecycle age must be positive")
    verification_time = (now or datetime.now(UTC)).astimezone(UTC)
    source = load_receipt(source_path, "source receipt")
    current_source = load_receipt(current_source_path, "current source receipt")
    source_delta = load_receipt(source_delta_path, "source delta receipt")
    restore = load_receipt(restore_path, "restore receipt")
    parity = load_receipt(parity_path, "parity receipt")
    reasoning = load_receipt(reasoning_path, "reasoning receipt")
    reasoning_compute_attestation = load_receipt(reasoning_compute_path, "reasoning compute attestation")
    cleanup = load_receipt(cleanup_path, "cleanup receipt")

    target_db = restore.get("target_database")
    run_id = restore.get("run_id")
    restore_source = restore.get("source") or {}
    capture_binding = restore_source.get("capture_receipt") or {}
    restore_target = restore.get("target") or {}
    restore_connectivity = restore_target.get("connectivity") or {}
    restore_connectivity_proof = restore_target.get("connectivity_proof") or {}
    restore_cloudsql = restore_target.get("cloudsql") or {}
    restore_compute = restore_target.get("compute") or {}
    restore_parity = restore.get("parity") or {}
    restore_service = restore.get("live_service") or {}
    parity_details = parity.get("details") or {}
    parity_evidence_checks = parity_evidence(parity_details)
    restore_inline_parity_checks = parity_evidence(restore_parity.get("details") or {})
    parity_connectivity = parity.get("private_connectivity") or {}
    parity_connectivity_proof = parity_connectivity.get("proof") or {}
    reasoning_checks = reasoning.get("checks") or {}
    reasoning_parity = reasoning.get("parity_receipt") or {}
    before_fingerprint = reasoning.get("database_fingerprint_before") or {}
    after_fingerprint = reasoning.get("database_fingerprint_after") or {}
    cleanup_database = cleanup.get("database") or {}
    cleanup_service = cleanup.get("live_service") or {}
    cleanup_cloudsql = (cleanup.get("target") or {}).get("cloudsql") or {}
    cleanup_compute = (cleanup.get("target") or {}).get("compute") or {}
    restore_contract = restore.get("execution_contract") or {}
    cleanup_contract = cleanup.get("execution_contract") or {}
    producer_reasoning_checks = blind_reasoning_evidence(reasoning, target_db)
    reasoning_compute = reasoning_compute_attestation.get("compute") or {}
    reasoning_compute_checks = reasoning_compute_attestation.get("checks") or {}
    reasoning_compute_mutation = reasoning_compute_attestation.get("mutation") or {}
    delta_baseline = source_delta.get("baseline") or {}
    delta_current = source_delta.get("current") or {}
    delta_details = source_delta.get("delta") or {}
    source_snapshot = source.get("snapshot") or {}
    current_source_snapshot = current_source.get("snapshot") or {}
    timeline_values = {
        "source_snapshot": source_snapshot.get("captured_at_utc"),
        "restore_started": restore.get("generated_at_utc"),
        "restore_completed": restore.get("completed_at_utc"),
        "parity_completed": parity.get("completed_at_utc"),
        "reasoning_started": reasoning.get("generated_at_utc"),
        "reasoning_completed": reasoning.get("completed_at_utc"),
        "reasoning_compute_attested": reasoning_compute_attestation.get("generated_at_utc"),
        "cleanup_started": cleanup.get("generated_at_utc"),
        "cleanup_completed": cleanup.get("completed_at_utc"),
        "current_source_snapshot": current_source_snapshot.get("captured_at_utc"),
    }
    timeline = [parse_timestamp(value, name) for name, value in timeline_values.items()]
    lifecycle_chronology = all(earlier <= later for earlier, later in pairwise(timeline))
    delta_detected = delta_details.get("delta_detected")
    delta_change_present = bool(
        delta_details.get("added_tables")
        or delta_details.get("removed_tables")
        or delta_details.get("changed_tables")
        or delta_details.get("changed_structures")
        or delta_details.get("total_row_delta")
    )
    current_source_time = parse_timestamp(
        current_source_snapshot.get("captured_at_utc"), "current source snapshot time"
    )
    restore_started_time = parse_timestamp(restore.get("generated_at_utc"), "restore start time")
    cleanup_completed_time = parse_timestamp(cleanup.get("completed_at_utc"), "cleanup completion time")
    postflight_age_seconds = (verification_time - current_source_time).total_seconds()
    lifecycle_age_seconds = (verification_time - cleanup_completed_time).total_seconds()
    lifecycle_span_seconds = (current_source_time - restore_started_time).total_seconds()
    postflight_after_lifecycle = current_source_time >= max(
        parse_timestamp(reasoning.get("completed_at_utc"), "reasoning completion time"),
        parse_timestamp(cleanup.get("completed_at_utc"), "cleanup completion time"),
    )

    required_reasoning_checks = (
        "exact_blind_prompt_without_ids",
        "claim_and_sources_retrieved",
        "discovery_show_evidence_completed",
        "retrieval_receipts_proven",
        "private_tls_read_only_target",
        "generated_database_unchanged",
        "canonical_counts_unchanged",
        "database_calls_read_only",
        "no_database_write",
        "no_telegram_send",
        "live_service_unchanged",
        "live_profile_unchanged",
        "temporary_profile_removed",
    )
    checks = {
        "source_schema_v2": source.get("schema") == SOURCE_SCHEMA and source.get("receipt_version") == 2,
        "source_pass": source.get("status") == "pass"
        and source.get("snapshot_exported") is True
        and (source.get("source_service") or {}).get("unchanged") is True
        and service_healthy((source.get("source_service") or {}).get("before"))
        and (source.get("source_service") or {}).get("before") == (source.get("source_service") or {}).get("after"),
        "source_authorized": bool(source.get("capture_authorization_ref")),
        "source_authorization_bound": capture_binding.get("capture_authorization_ref")
        == source.get("capture_authorization_ref"),
        "source_service_unchanged": source.get("service_unchanged") is True,
        "source_not_mutated": source.get("production_db_mutated") is False,
        "current_source_schema_v2": current_source.get("schema") == SOURCE_SCHEMA
        and current_source.get("receipt_version") == 2,
        "current_source_pass": current_source.get("status") == "pass"
        and current_source.get("snapshot_exported") is True
        and current_source.get("service_unchanged") is True
        and (current_source.get("source_service") or {}).get("unchanged") is True
        and service_healthy((current_source.get("source_service") or {}).get("before"))
        and (current_source.get("source_service") or {}).get("before")
        == (current_source.get("source_service") or {}).get("after")
        and current_source.get("production_db_mutated") is False,
        "current_source_identity": current_source.get("source") == source.get("source")
        and current_source_snapshot.get("system_identifier") == source_snapshot.get("system_identifier"),
        "source_delta_pass": source_delta.get("artifact") == "vps_canonical_snapshot_postflight_delta"
        and source_delta.get("schema") == DELTA_SCHEMA
        and source_delta.get("status") == "pass",
        "source_delta_receipts_bound": delta_baseline.get("capture_receipt_sha256") == sha256_file(source_path)
        and delta_current.get("capture_receipt_sha256") == sha256_file(current_source_path),
        "source_delta_manifests_bound": delta_baseline.get("manifest_sha256")
        == (source.get("manifest") or {}).get("sha256")
        and delta_current.get("manifest_sha256") == (current_source.get("manifest") or {}).get("sha256"),
        "source_delta_manifest_sql_bound": delta_baseline.get("manifest_sql_sha256")
        == (source.get("manifest") or {}).get("manifest_sql_sha256")
        and delta_current.get("manifest_sql_sha256")
        == (current_source.get("manifest") or {}).get("manifest_sql_sha256")
        and delta_baseline.get("manifest_sql_sha256") == delta_current.get("manifest_sql_sha256"),
        "source_delta_manifest_sql_reviewed": delta_baseline.get("manifest_sql_sha256") == REVIEWED_MANIFEST_SQL_SHA256
        and delta_current.get("manifest_sql_sha256") == REVIEWED_MANIFEST_SQL_SHA256
        and (source.get("manifest") or {}).get("manifest_sql_sha256") == REVIEWED_MANIFEST_SQL_SHA256
        and (current_source.get("manifest") or {}).get("manifest_sql_sha256") == REVIEWED_MANIFEST_SQL_SHA256
        and capture_binding.get("manifest_sql_sha256") == REVIEWED_MANIFEST_SQL_SHA256,
        "source_delta_authorizations_bound": delta_baseline.get("capture_authorization_ref")
        == source.get("capture_authorization_ref")
        and delta_current.get("capture_authorization_ref") == current_source.get("capture_authorization_ref"),
        "source_delta_explicit": isinstance(delta_details.get("delta_detected"), bool)
        and isinstance(delta_details.get("added_tables"), list)
        and isinstance(delta_details.get("removed_tables"), list)
        and isinstance(delta_details.get("changed_tables"), list)
        and isinstance(delta_details.get("changed_structures"), list)
        and isinstance(delta_details.get("total_row_delta"), int),
        "source_delta_baseline_shape": delta_baseline.get("run_id") == source.get("run_id")
        and delta_baseline.get("captured_at_utc") == source_snapshot.get("captured_at_utc")
        and delta_baseline.get("table_count") == (source.get("manifest") or {}).get("table_count")
        and delta_baseline.get("total_rows") == (source.get("manifest") or {}).get("total_rows")
        and bool(SHA256_RE.fullmatch(str(delta_baseline.get("table_fingerprint_sha256") or "")))
        and bool(SHA256_RE.fullmatch(str(delta_baseline.get("structure_fingerprint_sha256") or ""))),
        "source_delta_current_shape": delta_current.get("run_id") == current_source.get("run_id")
        and delta_current.get("captured_at_utc") == current_source_snapshot.get("captured_at_utc")
        and delta_current.get("table_count") == (current_source.get("manifest") or {}).get("table_count")
        and delta_current.get("total_rows") == (current_source.get("manifest") or {}).get("total_rows")
        and bool(SHA256_RE.fullmatch(str(delta_current.get("table_fingerprint_sha256") or "")))
        and bool(SHA256_RE.fullmatch(str(delta_current.get("structure_fingerprint_sha256") or ""))),
        "source_delta_consistent": delta_detected is delta_change_present
        and delta_details.get("total_row_delta")
        == (delta_current.get("total_rows") or 0) - (delta_baseline.get("total_rows") or 0)
        and (
            delta_detected is True
            or (
                delta_baseline.get("table_count") == delta_current.get("table_count")
                and delta_baseline.get("total_rows") == delta_current.get("total_rows")
                and delta_baseline.get("table_fingerprint_sha256") == delta_current.get("table_fingerprint_sha256")
                and delta_baseline.get("structure_fingerprint_sha256")
                == delta_current.get("structure_fingerprint_sha256")
            )
        ),
        "source_postflight_after_lifecycle": postflight_after_lifecycle,
        "source_postflight_fresh": 0 <= postflight_age_seconds <= max_postflight_age_seconds,
        "gcp_lifecycle_fresh": 0 <= lifecycle_age_seconds <= max_lifecycle_age_seconds,
        "lifecycle_span_bounded": 0 <= lifecycle_span_seconds <= max_lifecycle_age_seconds,
        "lifecycle_chronology": lifecycle_chronology,
        "restore_pass": restore.get("artifact") == "gcp_generated_postgres_snapshot_restore"
        and restore.get("status") == "pass",
        "restore_target_is_disposable": isinstance(target_db, str) and target_db.startswith("teleo_clone_"),
        "restore_run_id": isinstance(run_id, str) and run_id.startswith("gcp-restore-"),
        "source_receipt_hash_bound": capture_binding.get("sha256") == sha256_file(source_path),
        "source_provenance_bound": capture_binding.get("provenance_binding_sha256")
        == (source.get("provenance_binding") or {}).get("sha256"),
        "source_capture_checks": required_true_checks(
            capture_binding.get("checks"),
            {
                "artifact",
                "schema",
                "receipt_version",
                "status",
                "snapshot_exported",
                "service_unchanged",
                "source_service_healthy",
                "source_not_mutated",
                "telegram_not_sent",
                "run_id",
                "capture_authorization_ref",
                "source_identity",
                "source_database",
                "snapshot_identity",
                "dump_sha256",
                "dump_bytes",
                "source_manifest_sha256",
                "manifest_sql_sha256",
                "source_context_sha256",
                "table_count",
                "total_rows",
                "binding_algorithm",
                "binding_payload",
                "binding_sha256",
            },
        ),
        "source_dump_bound": capture_binding.get("dump_sha256") == (source.get("dump") or {}).get("sha256"),
        "source_execution_contract_bound": restore_contract.get("source") == source.get("source"),
        "source_manifest_bound": capture_binding.get("manifest_sha256") == (source.get("manifest") or {}).get("sha256"),
        "restore_manifest_matches_parity_source": capture_binding.get("manifest_sha256")
        == parity.get("source_manifest_sha256"),
        "restore_target_manifest_matches_parity": restore_target.get("manifest_sha256")
        == parity.get("target_manifest_sha256"),
        "restore_private_tls": restore_connectivity.get("private_connectivity") is True
        and restore_connectivity.get("ssl") is True
        and restore_connectivity.get("sslmode") == "verify-full"
        and restore_connectivity.get("server_identity_verified") is True
        and restore_connectivity.get("ssl_server_name") == restore_contract.get("ssl_server_name")
        and restore_connectivity.get("ssl_root_cert_sha256") == restore_contract.get("ssl_root_cert_sha256")
        and is_rfc1918(restore_connectivity.get("server_address")),
        "restore_public_ip_disabled": restore_cloudsql.get("public_ip_disabled") is True,
        "restore_cloudsql_checks": required_true_checks(
            restore_cloudsql.get("checks"),
            {
                "instance",
                "state",
                "postgres_16",
                "public_ip_disabled",
                "private_network",
                "expected_private_host",
                "no_non_private_address",
            },
        ),
        "restore_compute_checks": required_true_checks(
            restore_compute.get("checks"),
            {
                "project",
                "project_number",
                "expected_network_project",
                "instance",
                "instance_id",
                "zone",
                "network",
                "private_ip",
                "service_account",
            },
        ),
        "restore_compute_contract": restore_compute.get("project") == restore_contract.get("project")
        and restore_compute.get("instance") == restore_contract.get("compute_instance")
        and restore_compute.get("zone") == restore_contract.get("compute_zone")
        and restore_compute.get("expected_private_network") == restore_contract.get("private_network")
        and bool(restore_compute.get("network_resource"))
        and is_rfc1918(restore_compute.get("private_ip"))
        and restore_contract.get("restore_service_account") == DEFAULT_RESTORE_SERVICE_ACCOUNT
        and restore_compute.get("service_account") == DEFAULT_RESTORE_SERVICE_ACCOUNT,
        "restore_tls_identity_contract": bool(restore_contract.get("ssl_server_name"))
        and bool(SHA256_RE.fullmatch(str(restore_contract.get("ssl_root_cert_sha256") or ""))),
        "restore_connectivity_compute_bound": restore_connectivity.get("source_compute")
        == restore_compute.get("instance"),
        "restore_inline_parity": restore_parity.get("status") == "pass" and restore_parity.get("problems") == [],
        "restore_inline_parity_evidence": all(restore_inline_parity_checks.values()),
        "restore_service_healthy_unchanged": restore_service.get("unchanged") is True
        and service_healthy(restore_service.get("before"))
        and restore_service.get("before") == restore_service.get("after"),
        "parity_pass": parity.get("artifact") == "canonical_postgres_parity_verification"
        and parity.get("status") == "pass"
        and parity.get("scope") == "gcp_staging"
        and parity.get("problems") == [],
        "parity_target_bound": parity_details.get("target_database") == target_db,
        "parity_exact_evidence": all(parity_evidence_checks.values()),
        "parity_connectivity_hash_bound": restore_connectivity_proof.get("sha256")
        == parity.get("connectivity_proof_sha256"),
        "parity_private_proof": parity_connectivity.get("required") is True
        and parity_connectivity_proof.get("artifact") == "gcp_private_postgres_connectivity"
        and parity_connectivity_proof.get("status") == "pass"
        and parity_connectivity_proof.get("schema") == "livingip.gcpPrivatePostgresConnectivity.v2"
        and parity_connectivity_proof.get("restore_run_id") == run_id
        and parity_connectivity_proof.get("private_connectivity") is True
        and parity_connectivity_proof.get("public_ip_disabled") is True
        and parity_connectivity_proof.get("target_database") == target_db
        and parity_connectivity_proof.get("project") == restore_cloudsql.get("project")
        and parity_connectivity_proof.get("cloudsql_instance") == restore_cloudsql.get("instance")
        and parity_connectivity_proof.get("private_network") == restore_cloudsql.get("private_network")
        and parity_connectivity_proof.get("source_compute") == reasoning.get("source_compute")
        and parity_connectivity_proof.get("source_compute_instance_id") == restore_compute.get("instance_id")
        and parity_connectivity_proof.get("source_compute_zone") == restore_compute.get("zone")
        and is_rfc1918(parity_connectivity_proof.get("source_compute_private_ip"))
        and parity_connectivity_proof.get("server_address") == restore_connectivity.get("server_address")
        and parity_connectivity_proof.get("sslmode") == "verify-full"
        and parity_connectivity_proof.get("server_identity_verified") is True
        and parity_connectivity_proof.get("ssl_server_name") == restore_contract.get("ssl_server_name")
        and parity_connectivity_proof.get("ssl_root_cert_sha256") == restore_contract.get("ssl_root_cert_sha256")
        and required_true_checks(
            parity_connectivity_proof.get("cloudsql_control_plane_checks"),
            {
                "instance",
                "state",
                "postgres_16",
                "public_ip_disabled",
                "private_network",
                "expected_private_host",
                "no_non_private_address",
            },
        )
        and required_true_checks(
            parity_connectivity_proof.get("compute_identity_checks"),
            {
                "project",
                "project_number",
                "expected_network_project",
                "instance",
                "instance_id",
                "zone",
                "network",
                "private_ip",
                "service_account",
            },
        ),
        "reasoning_schema": reasoning.get("schema") == BLIND_REASONING_SCHEMA,
        "reasoning_pass": reasoning.get("status") == "pass" and not reasoning.get("errors"),
        "reasoning_target_bound": reasoning.get("target_database") == target_db,
        "reasoning_compute_bound": reasoning.get("source_compute") == restore_compute.get("instance"),
        "reasoning_parity_hash_bound": reasoning_parity.get("sha256") == sha256_file(parity_path),
        "reasoning_required_checks": all(reasoning_checks.get(name) is True for name in required_reasoning_checks),
        "reasoning_producer_evidence": all(producer_reasoning_checks.values()),
        "reasoning_compute_attestation": reasoning_compute_attestation.get("artifact")
        == "gcp_reasoning_compute_attestation"
        and reasoning_compute_attestation.get("schema") == REASONING_COMPUTE_SCHEMA
        and reasoning_compute_attestation.get("status") == "pass"
        and reasoning_compute_attestation.get("method") == "gce_metadata_server_v1"
        and nonempty_all_true(reasoning_compute_checks)
        and reasoning_compute_attestation.get("restore_run_id") == run_id
        and reasoning_compute_attestation.get("target_database") == target_db
        and all(value is False for value in reasoning_compute_mutation.values())
        and {
            "cloudsql_changed",
            "compute_changed",
            "database_changed",
            "telegram_message_sent",
        }
        <= set(reasoning_compute_mutation),
        "reasoning_compute_receipt_bound": (reasoning_compute_attestation.get("reasoning_receipt") or {}).get("sha256")
        == sha256_file(reasoning_path),
        "reasoning_compute_identity_bound": reasoning_compute.get("project") == restore_compute.get("project")
        and reasoning_compute.get("project_number") == restore_compute.get("project_number")
        and reasoning_compute.get("instance") == restore_compute.get("instance")
        and reasoning_compute.get("instance_id") == restore_compute.get("instance_id")
        and reasoning_compute.get("zone") == restore_compute.get("zone")
        and reasoning_compute.get("expected_private_network") == restore_compute.get("expected_private_network")
        and reasoning_compute.get("network_resource") == restore_compute.get("network_resource")
        and reasoning_compute.get("private_ip") == restore_compute.get("private_ip")
        and reasoning_compute.get("service_account") == restore_compute.get("service_account")
        and required_true_checks(
            reasoning_compute.get("checks"),
            {
                "project",
                "project_number",
                "expected_network_project",
                "instance",
                "instance_id",
                "zone",
                "network",
                "private_ip",
                "service_account",
            },
        ),
        "reasoning_no_send_no_write": reasoning.get("posted_to_telegram") is False
        and reasoning.get("database_write_attempted") is False,
        "reasoning_fingerprint_unchanged": bool(before_fingerprint.get("sha256"))
        and before_fingerprint.get("sha256") == after_fingerprint.get("sha256"),
        "cleanup_pass": cleanup.get("artifact") == "gcp_generated_postgres_snapshot_cleanup"
        and cleanup.get("status") == "pass",
        "cleanup_run_bound": cleanup.get("run_id") == run_id and cleanup.get("target_database") == target_db,
        "cleanup_restore_receipt_bound": (cleanup.get("restore_receipt") or {}).get("sha256")
        == sha256_file(restore_path),
        "cleanup_execution_contract_bound": bool(restore_contract) and cleanup_contract == restore_contract,
        "cleanup_cloudsql_bound": cleanup_cloudsql.get("project") == restore_cloudsql.get("project")
        and cleanup_cloudsql.get("instance") == restore_cloudsql.get("instance")
        and cleanup_cloudsql.get("expected_private_host") == restore_cloudsql.get("expected_private_host")
        and cleanup_cloudsql.get("expected_private_network") == restore_cloudsql.get("expected_private_network")
        and cleanup_cloudsql.get("public_ip_disabled") is True
        and required_true_checks(
            cleanup_cloudsql.get("checks"),
            {
                "instance",
                "state",
                "postgres_16",
                "public_ip_disabled",
                "private_network",
                "expected_private_host",
                "no_non_private_address",
            },
        ),
        "cleanup_compute_bound": cleanup_compute.get("instance_id") == restore_compute.get("instance_id")
        and cleanup_compute.get("project") == restore_compute.get("project")
        and cleanup_compute.get("project_number") == restore_compute.get("project_number")
        and cleanup_compute.get("instance") == restore_compute.get("instance")
        and cleanup_compute.get("zone") == restore_compute.get("zone")
        and cleanup_compute.get("expected_private_network") == restore_compute.get("expected_private_network")
        and cleanup_compute.get("network_resource") == restore_compute.get("network_resource")
        and cleanup_compute.get("private_ip") == restore_compute.get("private_ip")
        and cleanup_compute.get("service_account") == restore_compute.get("service_account")
        and required_true_checks(
            cleanup_compute.get("checks"),
            {
                "project",
                "project_number",
                "expected_network_project",
                "instance",
                "instance_id",
                "zone",
                "network",
                "private_ip",
                "service_account",
            },
        ),
        "clone_absent": cleanup_database.get("existed_before") is True
        and cleanup_database.get("clone_database_remaining") == 0,
        "cleanup_service_unchanged": cleanup_service.get("unchanged") is True
        and service_healthy(cleanup_service.get("before"))
        and cleanup_service.get("before") == cleanup_service.get("after"),
    }
    failed = sorted(name for name, passed in checks.items() if not passed)
    structurally_valid = not failed
    return {
        "artifact": "gcp_canonical_snapshot_lifecycle_verification",
        "generated_at_utc": verification_time.isoformat(),
        "status": "prepared_external_attestation_required" if structurally_valid else "fail",
        "current_tier": "T2_structural_receipt_bundle",
        "required_tier": "T3_live_private_gcp_staging",
        "t3_receipt_ready": False,
        "accepted_by_this_api": False,
        "target_database": target_db,
        "run_id": run_id,
        "checks": checks,
        "reasoning_producer_checks": producer_reasoning_checks,
        "restore_inline_parity_checks": restore_inline_parity_checks,
        "parity_evidence_checks": parity_evidence_checks,
        "post_snapshot_source_delta": delta_details,
        "timeline": timeline_values,
        "postflight_age_seconds": postflight_age_seconds,
        "max_postflight_age_seconds": max_postflight_age_seconds,
        "lifecycle_age_seconds": lifecycle_age_seconds,
        "lifecycle_span_seconds": lifecycle_span_seconds,
        "max_lifecycle_age_seconds": max_lifecycle_age_seconds,
        "failed_checks": failed,
        "receipt_sha256": {
            "source": sha256_file(source_path),
            "current_source": sha256_file(current_source_path),
            "source_delta": sha256_file(source_delta_path),
            "restore": sha256_file(restore_path),
            "parity": sha256_file(parity_path),
            "reasoning": sha256_file(reasoning_path),
            "reasoning_compute": sha256_file(reasoning_compute_path),
            "cleanup": sha256_file(cleanup_path),
        },
        "strongest_claim_allowed": (
            "caller-supplied lifecycle receipts are internally consistent; an independently authenticated platform receipt is still required"
            if structurally_valid
            else "lifecycle preparation is incomplete; failed checks must be repaired"
        ),
        "not_proven": ["production cutover", "ongoing replication", "Telegram delivery"],
    }


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--source-receipt", required=True, type=Path)
    parser.add_argument("--current-source-receipt", required=True, type=Path)
    parser.add_argument("--source-delta-receipt", required=True, type=Path)
    parser.add_argument("--restore-receipt", required=True, type=Path)
    parser.add_argument("--parity-receipt", required=True, type=Path)
    parser.add_argument("--reasoning-receipt", required=True, type=Path)
    parser.add_argument("--reasoning-compute-receipt", required=True, type=Path)
    parser.add_argument("--cleanup-receipt", required=True, type=Path)
    parser.add_argument("--output", required=True, type=Path)
    parser.add_argument("--max-postflight-age-seconds", default=900.0, type=float)
    parser.add_argument("--max-lifecycle-age-seconds", default=3600.0, type=float)
    args = parser.parse_args()
    try:
        payload = verify_lifecycle(
            source_path=args.source_receipt,
            current_source_path=args.current_source_receipt,
            source_delta_path=args.source_delta_receipt,
            restore_path=args.restore_receipt,
            parity_path=args.parity_receipt,
            reasoning_path=args.reasoning_receipt,
            reasoning_compute_path=args.reasoning_compute_receipt,
            cleanup_path=args.cleanup_receipt,
            max_postflight_age_seconds=args.max_postflight_age_seconds,
            max_lifecycle_age_seconds=args.max_lifecycle_age_seconds,
        )
    except (OSError, ValueError, KeyError, TypeError) as exc:
        payload = {
            "artifact": "gcp_canonical_snapshot_lifecycle_verification",
            "generated_at_utc": datetime.now(UTC).isoformat(),
            "status": "fail",
            "required_tier": "T3_live_private_gcp_staging",
            "failed_checks": ["receipt_load_or_validation_error"],
            "error": str(exc),
            "strongest_claim_allowed": "lifecycle incomplete; receipts could not be validated",
        }
    write_private_json(args.output, payload)
    print_private_receipt_summary(args.output, payload)
    return 0 if payload.get("status") == "prepared_external_attestation_required" else 1


if __name__ == "__main__":
    raise SystemExit(main())
