import argparse
import asyncio
import json
import os
import subprocess
from pathlib import Path

import pytest

from scripts.run_gcp_generated_db_direct_claim_suite import (
    GCP_HERMES_PYTHON,
    REVIEWED_CLOUDSQL_TOOL_SHA256,
    REVIEWED_MANIFEST_SQL_SHA256,
    audit_structured_count_readbacks,
    build_cloudsql_wrapper,
    database_fingerprint,
    direct_prompts,
    normalize_database_manifest_rows,
    normalize_stakeholder_labels,
    run_suite,
    should_reexec_in_hermes,
    validate_database_identity,
    validate_parity_receipt,
)
from scripts.run_leo_clone_bound_handler_checkpoint import (
    READ_ONLY_BRIDGE_COMMANDS,
    read_tool_proof,
    validate_read_only_bridge_argv,
)


def test_direct_prompt_catalog_is_the_exact_six_case_suite() -> None:
    prompts = direct_prompts()

    assert [prompt["id"] for prompt in prompts] == [f"DC-{index:02d}" for index in range(1, 7)]
    assert "status" in READ_ONLY_BRIDGE_COMMANDS
    validate_read_only_bridge_argv(["status"])
    validate_read_only_bridge_argv(["list-proposals", "--help"])
    validate_read_only_bridge_argv(["list-proposals", "--status", "rejected"])


def test_gcp_runner_reexecs_from_system_python_into_hermes_venv(tmp_path: Path) -> None:
    system_python = tmp_path / "system-python"
    hermes_python = tmp_path / "hermes-python"
    system_python.touch()
    hermes_python.touch()

    assert should_reexec_in_hermes(system_python, hermes_python) is True
    assert should_reexec_in_hermes(hermes_python, hermes_python) is False
    assert str(GCP_HERMES_PYTHON) == "/home/teleo/.hermes/hermes-agent/venv/bin/python"


def test_gcp_runner_reexecs_when_venv_python_symlinks_to_system_python(tmp_path: Path) -> None:
    system_python = tmp_path / "system-python"
    system_python.touch()
    hermes_python = tmp_path / "hermes-python"
    hermes_python.symlink_to(system_python)

    assert should_reexec_in_hermes(system_python, hermes_python) is True
    assert should_reexec_in_hermes(hermes_python, hermes_python) is False


def test_temporary_gcp_profile_removes_forbidden_stakeholder_alias() -> None:
    original = "For Cory use Next Cory-style follow-up."

    normalized = normalize_stakeholder_labels(original)

    assert normalized == "For m3taversal use Next m3taversal-style follow-up."
    assert "Cory" not in normalized


def test_structured_count_readback_audit_rejects_plausible_but_false_counts() -> None:
    status = {
        "high_signal_rows": {
            "claims": 1837,
            "sources": 4145,
            "claim_edges": 4916,
            "claim_evidence": 4670,
            "kb_proposals": 26,
        }
    }
    wrong = [
        {
            "prompt_id": "DC-03",
            "reply": (
                "DB readback: claims: `0`; sources: `0`; claim_edges: `0`; claim_evidence: `0`; kb_proposals: `26`."
            ),
        }
    ]
    correct = [
        {
            "prompt_id": "DC-03",
            "reply": (
                "DB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; "
                "claim_evidence: `4670`; kb_proposals: `26`."
            ),
        }
    ]

    assert audit_structured_count_readbacks(wrong, status)["all_observed_count_readbacks_match"] is False
    assert audit_structured_count_readbacks(correct, status)["all_observed_count_readbacks_match"] is True


def test_wrapper_binds_private_read_only_database_and_records_calls(tmp_path: Path) -> None:
    wrapper = build_cloudsql_wrapper(
        cloudsql_tool=tmp_path / "cloudsql_memory_tool.py",
        tool_log=tmp_path / "calls.jsonl",
        target_db="teleo_clone_test",
        host="10.61.0.3",
        project="teleo-501523",
        password_secret="test-secret-name",
        ssl_root_cert=tmp_path / "cloudsql-server-ca.pem",
        run_nonce="nonce",
    )

    assert "dbname=$TARGET_DATABASE" in wrapper
    assert '--db "$TARGET_DATABASE"' in wrapper
    assert '--canonical-db "$TARGET_DATABASE"' in wrapper
    assert "--credential-mode clone-readonly" in wrapper
    assert "--user postgres" in wrapper
    assert "sslmode=verify-ca" in wrapper
    assert "sslrootcert=$SSL_ROOT_CERT" in wrapper
    assert "begin transaction read only" in wrapper
    assert 'export PGOPTIONS="-c default_transaction_read_only=on"' in wrapper
    assert "default_transaction_read_only" in wrapper
    assert "host(inet_server_addr())" in wrapper
    assert '"phase": "start"' in wrapper
    assert '"phase": "end"' in wrapper
    assert "unset PGPASSWORD" in wrapper
    assert "systemctl restart" not in wrapper
    assert "send_message" not in wrapper


def test_database_identity_requires_private_tls_and_read_only_target() -> None:
    validate_database_identity(
        {
            "current_database": "teleo_clone_test",
            "system_identifier": "1234",
            "server_address": "10.61.0.3",
            "ssl": True,
            "transaction_read_only": "on",
            "default_transaction_read_only": "on",
        },
        "teleo_clone_test",
        "1234",
    )

    with pytest.raises(RuntimeError, match="not RFC1918-private"):
        validate_database_identity(
            {
                "current_database": "teleo_clone_test",
                "system_identifier": "1234",
                "server_address": "34.1.2.3",
                "ssl": True,
                "transaction_read_only": "on",
                "default_transaction_read_only": "on",
            },
            "teleo_clone_test",
            "1234",
        )


def test_manifest_normalization_excludes_nondeterministic_timing() -> None:
    output = "\n".join(
        [
            "BEGIN",
            '{"kind":"identity","database":"teleo_clone_test","captured_at":"2026-07-12T00:00:00Z"}',
            '{"kind":"schemas","items":["kb_stage","public"]}',
            '{"kind":"table","schema":"public","table":"claims","row_count":2,"rowset_md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}',
            '{"kind":"performance","query":"count_claims","elapsed_ms":1.2,"observed_rows":2}',
            "ROLLBACK",
        ]
    )

    rows = normalize_database_manifest_rows(output)

    assert all(row.get("kind") != "performance" for row in rows)
    assert next(row for row in rows if row["kind"] == "identity").get("captured_at") is None


def test_database_fingerprint_uses_full_normalized_manifest(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    manifest_sql = tmp_path / "manifest.sql"
    manifest_sql.write_text("select 1;\n")
    args = type(
        "Args",
        (),
        {
            "host": "10.61.0.3",
            "target_db": "teleo_clone_test",
            "manifest_sql": manifest_sql,
            "ssl_root_cert": tmp_path / "cloudsql-server-ca.pem",
        },
    )()
    output = "\n".join(
        [
            '{"kind":"identity","database":"teleo_clone_test","server_address":"10.61.0.3",'
            '"ssl":true,"transaction_read_only":"on","captured_at":"one"}',
            '{"kind":"schemas","items":["kb_stage","public"]}',
            '{"kind":"table","schema":"public","table":"claims","row_count":2,"rowset_md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}',
        ]
    )
    monkeypatch.setattr("scripts.run_gcp_generated_db_direct_claim_suite.database_password", lambda _args: "secret")
    monkeypatch.setattr("scripts.run_gcp_generated_db_direct_claim_suite.run", lambda *_args, **_kwargs: output)

    receipt = database_fingerprint(args)

    assert receipt["table_count"] == 1
    assert receipt["total_rows"] == 2
    assert receipt["database"] == "teleo_clone_test"
    assert len(receipt["sha256"]) == 64


def test_reviewed_helper_hashes_match_repo_files() -> None:
    import hashlib

    assert (
        hashlib.sha256(Path("hermes-agent/leoclean-bin/cloudsql_memory_tool.py").read_bytes()).hexdigest()
        == REVIEWED_CLOUDSQL_TOOL_SHA256
    )
    assert (
        hashlib.sha256(Path("ops/postgres_parity_manifest.sql").read_bytes()).hexdigest()
        == REVIEWED_MANIFEST_SQL_SHA256
    )


def parity_payload(target_db: str = "teleo_clone_test") -> dict[str, object]:
    return {
        "artifact": "canonical_postgres_parity_verification",
        "status": "pass",
        "scope": "local_restore",
        "problems": [],
        "details": {
            "target_database": target_db,
            "source_table_count": 2,
            "target_table_count": 2,
            "source_total_rows": 3,
            "target_total_rows": 3,
            "table_mismatches": [],
            "application_role_mismatches": {},
            "required_extension_mismatches": {},
            "performance_problems": [],
            "structural_hashes": {"columns": {"source": "same", "target": "same"}},
        },
    }


def test_parity_receipt_is_bound_to_generated_target(tmp_path: Path) -> None:
    receipt_path = tmp_path / "parity.json"
    receipt_path.write_text(json.dumps(parity_payload()))

    receipt = validate_parity_receipt(receipt_path, "teleo_clone_test")

    assert receipt["target_database"] == "teleo_clone_test"
    assert receipt["target_table_count"] == 2

    with pytest.raises(RuntimeError, match="target_database"):
        validate_parity_receipt(receipt_path, "teleo_clone_other")


def test_generated_wrapper_executes_only_clone_bound_default_read_only_tool(tmp_path: Path) -> None:
    fake_bin = tmp_path / "bin"
    fake_bin.mkdir()
    gcloud = fake_bin / "gcloud"
    gcloud.write_text("#!/bin/sh\nprintf 'fake-password\\n'\n")
    gcloud.chmod(0o700)
    psql = fake_bin / "psql"
    psql.write_text(
        "#!/bin/sh\ncat >/dev/null\nprintf '%s\\n' "
        '\'{"current_database":"teleo_clone_test","system_identifier":"system-1",'
        '"server_address":"10.61.0.3","ssl":true,"transaction_read_only":"on",'
        '"default_transaction_read_only":"on"}\'\n'
    )
    psql.chmod(0o700)
    tool = tmp_path / "tool.py"
    tool.write_text(
        "import json, os, sys\n"
        "print(json.dumps({'argv': sys.argv[1:], 'pgoptions': os.environ.get('PGOPTIONS'), "
        "'has_password': bool(os.environ.get('PGPASSWORD'))}))\n"
    )
    log = tmp_path / "calls.jsonl"
    wrapper = tmp_path / "teleo-kb"
    wrapper.write_text(
        build_cloudsql_wrapper(
            cloudsql_tool=tool,
            tool_log=log,
            target_db="teleo_clone_test",
            host="10.61.0.3",
            project="teleo-501523",
            password_secret="test-secret",
            ssl_root_cert=tmp_path / "cloudsql-server-ca.pem",
            run_nonce="test-nonce",
            system_exec_path=f"{fake_bin}:/usr/bin:/bin",
        )
    )
    wrapper.chmod(0o700)

    completed = subprocess.run(
        [str(wrapper), "decision-matrix-status"],
        text=True,
        capture_output=True,
        env={**os.environ, "HOME": str(tmp_path)},
        check=False,
    )

    assert completed.returncode == 0, completed.stderr
    tool_receipt = json.loads(completed.stdout)
    assert tool_receipt["pgoptions"] == "-c default_transaction_read_only=on"
    assert tool_receipt["has_password"] is True
    assert tool_receipt["argv"][:6] == [
        "--host",
        "10.61.0.3",
        "--db",
        "teleo_clone_test",
        "--canonical-db",
        "teleo_clone_test",
    ]
    assert tool_receipt["argv"][tool_receipt["argv"].index("--credential-mode") + 1] == "clone-readonly"
    assert tool_receipt["argv"][tool_receipt["argv"].index("--user") + 1] == "postgres"
    events = [json.loads(line) for line in log.read_text().splitlines()]
    assert [event["phase"] for event in events] == ["start", "end"]
    assert events[0]["database_identity"]["default_transaction_read_only"] == "on"
    assert "fake-password" not in log.read_text()
    proof = read_tool_proof(
        log,
        "teleo-prod-1",
        "teleo_clone_test",
        run_nonce="test-nonce",
        database_identity={"system_identifier": "system-1"},
        require_database_read_only=True,
        require_default_read_only=True,
    )
    assert proof["all_bound_to_supplied_target"] is True
    events[0]["database_identity"]["default_transaction_read_only"] = "off"
    unsafe_log = tmp_path / "unsafe-calls.jsonl"
    unsafe_log.write_text("".join(json.dumps(event) + "\n" for event in events))
    unsafe_proof = read_tool_proof(
        unsafe_log,
        "teleo-prod-1",
        "teleo_clone_test",
        run_nonce="test-nonce",
        database_identity={"system_identifier": "system-1"},
        require_database_read_only=True,
        require_default_read_only=True,
    )
    assert unsafe_proof["all_bound_to_supplied_target"] is False


def test_run_suite_retains_failure_report_when_postflight_fails(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    output = tmp_path / "report.json"
    placeholder = tmp_path / "placeholder"
    placeholder.write_text("x")
    args = argparse.Namespace(
        target_db="teleo_clone_test",
        cloudsql_tool=placeholder,
        manifest_sql=placeholder,
        parity_receipt=placeholder,
        service="leo.service",
        live_profile=tmp_path,
        output=output,
        chat_id="1",
        user_id="2",
        user_name="test",
        run_id="test-run",
        turn_timeout=1,
        host="10.61.0.3",
        project="teleo-501523",
        password_secret="secret-name",
    )
    identity_calls = 0

    def fake_identity(_args: argparse.Namespace) -> dict[str, object]:
        nonlocal identity_calls
        identity_calls += 1
        if identity_calls > 1:
            raise RuntimeError("postflight unavailable")
        return {
            "current_database": "teleo_clone_test",
            "system_identifier": "system-1",
            "server_address": "10.61.0.3",
            "ssl": True,
            "transaction_read_only": "on",
            "default_transaction_read_only": "on",
        }

    monkeypatch.setattr("scripts.run_gcp_generated_db_direct_claim_suite.validate_reviewed_file", lambda *args: "hash")
    monkeypatch.setattr(
        "scripts.run_gcp_generated_db_direct_claim_suite.validate_parity_receipt",
        lambda *args: {"target_database": "teleo_clone_test"},
    )
    monkeypatch.setattr("scripts.run_gcp_generated_db_direct_claim_suite.service_state", lambda *args: {"MainPID": "1"})
    monkeypatch.setattr(
        "scripts.run_gcp_generated_db_direct_claim_suite.profile_hashes",
        lambda *args: {"wrapper": "hash"},
    )
    monkeypatch.setattr("scripts.run_gcp_generated_db_direct_claim_suite.database_identity", fake_identity)
    monkeypatch.setattr(
        "scripts.run_gcp_generated_db_direct_claim_suite.database_fingerprint",
        lambda *args: {"sha256": "fingerprint"},
    )
    monkeypatch.setattr(
        "scripts.run_gcp_generated_db_direct_claim_suite.canonical_status",
        lambda *args: {"high_signal_rows": {"claims": 1}},
    )
    monkeypatch.setattr(
        "scripts.run_gcp_generated_db_direct_claim_suite.bound.copy_profile",
        lambda *args, **kwargs: (_ for _ in ()).throw(RuntimeError("runner setup failed")),
    )

    report = asyncio.run(run_suite(args))
    retained = json.loads(output.read_text())

    assert report["status"] == "fail"
    assert retained["status"] == "fail"
    assert {error["phase"] for error in retained["errors"]} >= {"execution", "database_identity_after"}
    assert "gateway_child_cleanup" in retained
