import hashlib
import json
from pathlib import Path

import pytest

from ops.capture_vps_canonical_postgres_snapshot import (
    SOURCE_SNAPSHOT_RECEIPT_SCHEMA,
    build_provenance_binding,
)
from ops.verify_postgres_parity_manifest import REVIEWED_MANIFEST_SQL_SHA256
from ops.verify_vps_canonical_snapshot_delta import verify_delta, write_private_json


def manifest_text(*, claims: int = 1, constraint_name: str = "claims_pkey") -> str:
    rows = [
        {
            "kind": "identity",
            "database": "teleo",
            "server_version_num": 160014,
            "transaction_read_only": "on",
        },
        {"kind": "schemas", "items": ["kb_stage", "public"]},
        {"kind": "extensions", "items": [{"name": "pgcrypto", "version": "1.3"}]},
        {
            "kind": "application_roles",
            "items": [
                {"name": "kb_apply", "can_login": True},
                {"name": "kb_gate_owner", "can_login": False},
                {"name": "kb_review", "can_login": True},
            ],
        },
        {"kind": "role_memberships", "items": []},
        {"kind": "object_ownership", "items": []},
        {"kind": "object_acl", "items": []},
        {"kind": "columns", "items": []},
        {"kind": "constraints", "items": [{"name": constraint_name}]},
        {"kind": "indexes", "items": []},
        {"kind": "sequences", "items": []},
        {"kind": "views", "items": []},
        {"kind": "functions", "items": []},
        {"kind": "triggers", "items": []},
        {"kind": "types", "items": []},
        {"kind": "policies", "items": []},
        {
            "kind": "table",
            "schema": "public",
            "table": "claims",
            "row_count": claims,
            "rowset_md5": hashlib.md5(str(claims).encode(), usedforsecurity=False).hexdigest(),
        },
        {"kind": "performance", "query": "count_claims", "elapsed_ms": 1.0, "observed_rows": claims},
    ]
    return "".join(json.dumps(row, sort_keys=True) + "\n" for row in rows)


def capture_fixture(
    root: Path,
    *,
    name: str,
    captured_at: str,
    authorization_ref: str,
    claims: int = 1,
    constraint_name: str = "claims_pkey",
) -> tuple[Path, Path]:
    manifest = root / f"{name}-manifest.jsonl"
    manifest.write_text(manifest_text(claims=claims, constraint_name=constraint_name), encoding="utf-8")
    dump_sha256 = hashlib.sha256(f"{name}-dump".encode()).hexdigest()
    manifest_sha256 = hashlib.sha256(manifest.read_bytes()).hexdigest()
    manifest_sql_sha256 = REVIEWED_MANIFEST_SQL_SHA256
    source_context_sha256 = "3" * 64
    source = {
        "ssh_target": "root@77.42.65.182",
        "container": "teleo-pg",
        "database": "teleo",
        "service": "leoclean-gateway.service",
    }
    healthy_service = {
        "ActiveState": "active",
        "SubState": "running",
        "MainPID": "148735",
        "NRestarts": "0",
    }
    snapshot = {
        "exported_snapshot_id": "00000003-000001A2-1",
        "txid_snapshot": "100:120:105,111",
        "wal_lsn": "0/16B6C50",
        "system_identifier": "7649789040005668902",
        "captured_at_utc": captured_at,
    }
    receipt = {
        "artifact": "vps_canonical_postgres_exported_snapshot",
        "schema": SOURCE_SNAPSHOT_RECEIPT_SCHEMA,
        "receipt_version": 2,
        "status": "pass",
        "run_id": name,
        "capture_authorization_ref": authorization_ref,
        "source": source,
        "snapshot_exported": True,
        "snapshot": snapshot,
        "dump": {"sha256": dump_sha256},
        "manifest": {
            "sha256": manifest_sha256,
            "manifest_sql_sha256": manifest_sql_sha256,
            "table_count": 1,
            "total_rows": claims,
        },
        "source_context": {"sha256": source_context_sha256},
        "provenance_binding": build_provenance_binding(
            run_id=name,
            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_unchanged": True,
        "source_service": {"before": healthy_service, "after": healthy_service, "unchanged": True},
        "production_db_mutated": False,
        "telegram_send_attempted": False,
    }
    receipt_path = root / f"{name}-receipt.json"
    receipt_path.write_text(json.dumps(receipt, sort_keys=True) + "\n", encoding="utf-8")
    return receipt_path, manifest


def test_postflight_no_delta_is_bound_and_explicit(tmp_path: Path) -> None:
    baseline_receipt, baseline_manifest = capture_fixture(
        tmp_path,
        name="canonical-baseline",
        captured_at="2026-07-15T01:00:00+00:00",
        authorization_ref="codex-delegation:baseline123",
    )
    current_receipt, current_manifest = capture_fixture(
        tmp_path,
        name="canonical-postflight",
        captured_at="2026-07-15T02:00:00+00:00",
        authorization_ref="codex-delegation:postflight123",
    )

    result = verify_delta(
        baseline_receipt_path=baseline_receipt,
        baseline_manifest_path=baseline_manifest,
        baseline_authorization_ref="codex-delegation:baseline123",
        current_receipt_path=current_receipt,
        current_manifest_path=current_manifest,
        current_authorization_ref="codex-delegation:postflight123",
    )

    assert result["status"] == "pass"
    assert result["delta"]["delta_detected"] is False
    assert result["delta"]["total_row_delta"] == 0


def test_postflight_data_and_structure_delta_is_reported_not_hidden(tmp_path: Path) -> None:
    baseline_receipt, baseline_manifest = capture_fixture(
        tmp_path,
        name="canonical-baseline",
        captured_at="2026-07-15T01:00:00+00:00",
        authorization_ref="codex-delegation:baseline123",
    )
    current_receipt, current_manifest = capture_fixture(
        tmp_path,
        name="canonical-postflight",
        captured_at="2026-07-15T02:00:00+00:00",
        authorization_ref="codex-delegation:postflight123",
        claims=2,
        constraint_name="claims_pkey_v2",
    )

    result = verify_delta(
        baseline_receipt_path=baseline_receipt,
        baseline_manifest_path=baseline_manifest,
        baseline_authorization_ref="codex-delegation:baseline123",
        current_receipt_path=current_receipt,
        current_manifest_path=current_manifest,
        current_authorization_ref="codex-delegation:postflight123",
    )

    assert result["status"] == "pass"
    assert result["delta"]["delta_detected"] is True
    assert result["delta"]["total_row_delta"] == 1
    assert result["delta"]["changed_tables"][0]["table"] == "public.claims"
    assert result["delta"]["changed_structures"][0]["kind"] == "constraints"


def test_postflight_rejects_wrong_authorization_or_stale_capture(tmp_path: Path) -> None:
    baseline_receipt, baseline_manifest = capture_fixture(
        tmp_path,
        name="canonical-baseline",
        captured_at="2026-07-15T02:00:00+00:00",
        authorization_ref="codex-delegation:baseline123",
    )
    current_receipt, current_manifest = capture_fixture(
        tmp_path,
        name="canonical-postflight",
        captured_at="2026-07-15T01:00:00+00:00",
        authorization_ref="codex-delegation:postflight123",
    )

    with pytest.raises(ValueError, match="newer than"):
        verify_delta(
            baseline_receipt_path=baseline_receipt,
            baseline_manifest_path=baseline_manifest,
            baseline_authorization_ref="codex-delegation:baseline123",
            current_receipt_path=current_receipt,
            current_manifest_path=current_manifest,
            current_authorization_ref="codex-delegation:postflight123",
        )

    with pytest.raises(ValueError, match="authorization"):
        verify_delta(
            baseline_receipt_path=baseline_receipt,
            baseline_manifest_path=baseline_manifest,
            baseline_authorization_ref="codex-delegation:wrong123",
            current_receipt_path=current_receipt,
            current_manifest_path=current_manifest,
            current_authorization_ref="codex-delegation:postflight123",
        )


def test_postflight_rejects_unchanged_but_unhealthy_service(tmp_path: Path) -> None:
    baseline_receipt, baseline_manifest = capture_fixture(
        tmp_path,
        name="canonical-baseline",
        captured_at="2026-07-15T01:00:00+00:00",
        authorization_ref="codex-delegation:baseline123",
    )
    current_receipt, current_manifest = capture_fixture(
        tmp_path,
        name="canonical-postflight",
        captured_at="2026-07-15T02:00:00+00:00",
        authorization_ref="codex-delegation:postflight123",
    )
    current = json.loads(current_receipt.read_text())
    current["source_service"]["before"]["ActiveState"] = "failed"
    current["source_service"]["after"]["ActiveState"] = "failed"
    current_receipt.write_text(json.dumps(current, sort_keys=True) + "\n", encoding="utf-8")

    with pytest.raises(ValueError, match="source_service_healthy"):
        verify_delta(
            baseline_receipt_path=baseline_receipt,
            baseline_manifest_path=baseline_manifest,
            baseline_authorization_ref="codex-delegation:baseline123",
            current_receipt_path=current_receipt,
            current_manifest_path=current_manifest,
            current_authorization_ref="codex-delegation:postflight123",
        )


def test_postflight_rejects_a_different_manifest_algorithm_even_when_rebound(tmp_path: Path) -> None:
    baseline_receipt, baseline_manifest = capture_fixture(
        tmp_path,
        name="canonical-baseline",
        captured_at="2026-07-15T01:00:00+00:00",
        authorization_ref="codex-delegation:baseline123",
    )
    current_receipt, current_manifest = capture_fixture(
        tmp_path,
        name="canonical-postflight",
        captured_at="2026-07-15T02:00:00+00:00",
        authorization_ref="codex-delegation:postflight123",
    )
    current = json.loads(current_receipt.read_text())
    current["manifest"]["manifest_sql_sha256"] = "f" * 64
    current["provenance_binding"] = build_provenance_binding(
        run_id=current["run_id"],
        authorization_ref=current["capture_authorization_ref"],
        source=current["source"],
        snapshot=current["snapshot"],
        dump_sha256=current["dump"]["sha256"],
        manifest_sql_sha256="f" * 64,
        source_manifest_sha256=current["manifest"]["sha256"],
        source_context_sha256=current["source_context"]["sha256"],
    )
    current_receipt.write_text(json.dumps(current, sort_keys=True) + "\n", encoding="utf-8")

    with pytest.raises(ValueError, match="manifest_sql_reviewed"):
        verify_delta(
            baseline_receipt_path=baseline_receipt,
            baseline_manifest_path=baseline_manifest,
            baseline_authorization_ref="codex-delegation:baseline123",
            current_receipt_path=current_receipt,
            current_manifest_path=current_manifest,
            current_authorization_ref="codex-delegation:postflight123",
        )


def test_delta_receipt_writer_keeps_private_mode(tmp_path: Path) -> None:
    output = tmp_path / "private" / "delta.json"

    write_private_json(output, {"status": "pass"})

    assert output.stat().st_mode & 0o777 == 0o600
    assert output.parent.stat().st_mode & 0o777 == 0o700


def test_delta_receipt_writer_does_not_chmod_an_existing_parent(tmp_path: Path) -> None:
    parent = tmp_path / "existing"
    parent.mkdir(mode=0o755)
    output = parent / "delta.json"

    write_private_json(output, {"status": "pass"})

    assert parent.stat().st_mode & 0o777 == 0o755
    assert output.stat().st_mode & 0o777 == 0o600
