from __future__ import annotations

import hashlib
import json
import subprocess

from scripts import run_leo_direct_claim_handler_suite as suite


def response_binding_helper():
    namespace = {"hashlib": hashlib}
    exec(suite.RESPONSE_BINDING_HELPER_SOURCE, namespace)
    return namespace["response_bindings_are_hash_bound"]


def safe_report() -> dict:
    return {
        "remote_returncode": 0,
        "pass_runtime": True,
        "handler": {"authorized": True},
        "results": [
            {
                "ok": True,
                "mutates_kb": False,
                "database_tool_trace": {
                    "database_tool_call_count": 1,
                    "database_tool_completed_count": 1,
                    "database_tool_calls_read_only": True,
                    "calls": [
                        {
                            "database_invocations": [{"access_mode": "read_only"}],
                            "result": {
                                "content_sha256": "a" * 64,
                                "error_detected": False,
                                "nonempty": True,
                            },
                        }
                    ],
                },
            }
        ],
        "posted_to_telegram": False,
        "mutates_kb_by_harness": False,
        "db_counts_changed": False,
        "db_fingerprint_unchanged": True,
        "live_behavior_manifest_unchanged": True,
        "service_before_after": {"unchanged_from_preexisting_live_readback": True},
        "temp_profile_removed": True,
        "model_call_trace_all_bound": True,
        "execution_manifest_summary": {"all_turns_attribution_complete": True},
    }


def test_redaction_preserves_json_and_removes_secret_values() -> None:
    raw = json.dumps(
        {
            "bot_token": "123456789:abcdefghijklmnopqrstuvwxyz_ABCD",
            "api_key": "sk-live-secret",
            "nested": {"password": "hunter2", "access_token": "bearer-secret"},
            "message": "Authorization: Bearer top-secret-value",
        }
    )

    redacted = suite.redact(raw)
    parsed = json.loads(redacted)

    assert parsed["bot_token"] == "[REDACTED]"
    assert parsed["api_key"] == "[REDACTED]"
    assert parsed["nested"]["password"] == "[REDACTED]"
    assert parsed["nested"]["access_token"] == "[REDACTED]"
    assert parsed["message"] == "Authorization: Bearer [REDACTED]"
    assert "hunter2" not in redacted
    assert "top-secret-value" not in redacted


def test_successful_stdout_is_redacted_before_json_parsing(monkeypatch) -> None:
    secret = "123456789:abcdefghijklmnopqrstuvwxyz_ABCD"
    calls = iter(
        [
            subprocess.CompletedProcess([], 0, stdout=json.dumps({"bot_token": secret}) + "\n", stderr=""),
            subprocess.CompletedProcess([], 0, stdout="", stderr=""),
        ]
    )
    monkeypatch.setattr(suite.subprocess, "run", lambda *args, **kwargs: next(calls))

    result = suite.run_remote(prompts=[])

    assert result["parsed"]["bot_token"] == "[REDACTED]"
    assert secret not in json.dumps(result)


def test_successful_stdout_survives_remote_report_cleanup_timeout(monkeypatch) -> None:
    completed = subprocess.CompletedProcess([], 0, stdout=json.dumps({"run_id": "complete"}) + "\n", stderr="")
    calls = 0

    def fake_run(*args, **kwargs):
        nonlocal calls
        calls += 1
        if calls == 1:
            return completed
        raise subprocess.TimeoutExpired(args[0], timeout=180)

    monkeypatch.setattr(suite.subprocess, "run", fake_run)

    result = suite.run_remote(prompts=[])

    assert result["parsed"] == {"run_id": "complete"}
    assert result["remote_report_cleanup_confirmed"] is False


def test_safety_gate_passes_only_for_complete_no_send_no_write_run() -> None:
    report = safe_report()

    assert suite.report_passes_safety(report) is True
    assert suite.build_safety_gate(report)["failed_checks"] == []


def test_safety_gate_rejects_write_capable_tool_and_incomplete_receipt() -> None:
    report = safe_report()
    report["results"][0]["database_tool_trace"]["database_tool_calls_read_only"] = False
    report["results"][0]["database_tool_trace"]["calls"][0]["database_invocations"][0]["access_mode"] = "write"
    report["execution_manifest_summary"]["all_turns_attribution_complete"] = False

    gate = suite.build_safety_gate(report)

    assert gate["status"] == "fail"
    assert "all_tool_traces_read_only_and_bound" in gate["failed_checks"]
    assert "all_turn_manifests_complete" in gate["failed_checks"]


def test_safety_gate_rejects_missing_no_send_declaration() -> None:
    report = safe_report()
    report.pop("posted_to_telegram")

    gate = suite.build_safety_gate(report)

    assert gate["status"] == "fail"
    assert "no_telegram_post" in gate["failed_checks"]


def test_response_binding_helper_requires_exact_prompt_and_delivered_reply_hashes() -> None:
    prompt = "Broad ID-free read-only question"
    reply = "A bounded grounded answer"
    result = {"prompt": prompt, "reply": reply}
    record = {
        "status": "ok",
        "validated": True,
        "query_sha256": hashlib.sha256(prompt.encode()).hexdigest(),
        "response_sha256": "a" * 64,
        "delivered_response_sha256": hashlib.sha256(reply.encode()).hexdigest(),
    }

    helper = response_binding_helper()
    assert helper([result], [record]) is True

    for field in ("query_sha256", "response_sha256", "delivered_response_sha256"):
        missing = dict(record)
        missing.pop(field)
        assert helper([result], [missing]) is False

    mismatched = dict(record, delivered_response_sha256="b" * 64)
    assert helper([result], [mismatched]) is False
