#!/usr/bin/env python3
"""Build an exact authorization packet for a Telegram-visible DC benchmark run.

The packet is a planner, not a sender. It records the raw no-context DC-01..DC-06
messages, the target Telegram group, the expected proof artifacts, and the
required authorization sentence. It never sends Telegram messages and never
mutates the database.
"""

from __future__ import annotations

import argparse
import json
import subprocess
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

import working_leo_open_ended_benchmark as benchmark

REPORT_DIR = Path("docs/reports/leo-working-state-20260709")
DEFAULT_OUT = REPORT_DIR / "telegram-visible-direct-claim-authorization-packet-current.json"
DEFAULT_MARKDOWN_OUT = REPORT_DIR / "telegram-visible-direct-claim-authorization-packet-current.md"
DEFAULT_LIVE_OPEN_SCORE = REPORT_DIR / "telegram-live-open-ended-suite-score-current.json"
DEFAULT_HANDLER_SUITE = REPORT_DIR / "telegram-handler-direct-claim-suite-current.json"
DEFAULT_HANDLER_SCORE = REPORT_DIR / "telegram-handler-direct-claim-suite-score-current.json"
DEFAULT_GCP_PROBE = REPORT_DIR / "gcp-db-parity-current-probe-current.json"
DEFAULT_CAPTURE_RECEIPT = REPORT_DIR / "telegram-visible-direct-claim-capture-receipt-current.json"
DEFAULT_CAPTURE_RECEIPT_MARKDOWN = REPORT_DIR / "telegram-visible-direct-claim-capture-receipt-current.md"
DEFAULT_CAPTURE_RESULTS = REPORT_DIR / "telegram-visible-direct-claim-capture-results-current.json"
DEFAULT_CHAT_ID = "-5146042086"
DEFAULT_CHAT_LABEL = "Leo"
DEFAULT_PROOF_DIR = "outputs/telegram-visible-direct-claim-suite-20260710"


def _load_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8"))


def _path_status(path: Path) -> dict[str, Any]:
    exists = path.exists()
    return {"path": str(path), "exists": exists, "bytes": path.stat().st_size if exists else 0}


def current_git_sha(repo_root: Path = Path(".")) -> str:
    proc = subprocess.run(
        ["git", "rev-parse", "HEAD"],
        cwd=repo_root,
        text=True,
        capture_output=True,
        check=False,
    )
    return proc.stdout.strip() if proc.returncode == 0 else ""


def direct_claim_messages() -> list[dict[str, Any]]:
    return [
        {
            "prompt_id": prompt["id"],
            "dimension": prompt["dimension"],
            "message": prompt["message"],
            "expected_answer": prompt["expected_answer"],
            "expected_follow_up": prompt["expected_follow_up"],
            "required_signals": list(prompt["required_signals"]),
        }
        for prompt in benchmark.M3TAVERSAL_DIRECT_CLAIM_FOLLOWUP_SCENARIOS
    ]


def _score_passes(score: dict[str, Any], *, expected_count: int) -> bool:
    score_block = score.get("score", {})
    return (
        score_block.get("pass") is True
        and score_block.get("passes") == score_block.get("expected_prompt_count") == expected_count
        and score_block.get("failures") == []
        and score_block.get("missing_prompt_ids") == []
        and score_block.get("unexpected_prompt_ids") == []
    )


def _live_open_score_passes(score: dict[str, Any]) -> bool:
    score_block = score.get("score", {})
    return score_block.get("pass") is True and score_block.get("passes") == score_block.get(
        "expected_prompt_count"
    )


def _handler_suite_is_safe(suite: dict[str, Any], *, expected_count: int) -> bool:
    return (
        suite.get("pass_runtime") is True
        and suite.get("posted_to_telegram") is False
        and suite.get("changed_live_profile") is False
        and suite.get("mutates_kb_by_harness") is False
        and suite.get("db_counts_changed") is False
        and suite.get("temp_profile_removed") is True
        and len(suite.get("results", [])) == expected_count
    )


def build_packet(
    *,
    live_open_score_path: Path = DEFAULT_LIVE_OPEN_SCORE,
    handler_suite_path: Path = DEFAULT_HANDLER_SUITE,
    handler_score_path: Path = DEFAULT_HANDLER_SCORE,
    gcp_probe_path: Path = DEFAULT_GCP_PROBE,
    chat_id: str = DEFAULT_CHAT_ID,
    chat_label: str = DEFAULT_CHAT_LABEL,
    proof_dir: str = DEFAULT_PROOF_DIR,
    git_sha: str | None = None,
) -> dict[str, Any]:
    messages = direct_claim_messages()
    live_open_score = _load_json(live_open_score_path)
    handler_suite = _load_json(handler_suite_path)
    handler_score = _load_json(handler_score_path)
    gcp_probe = _load_json(gcp_probe_path) if gcp_probe_path.exists() else {}
    sha = git_sha if git_sha is not None else current_git_sha()
    expected_ids = [item["prompt_id"] for item in messages]

    evidence_paths = {
        "live_open_score": _path_status(live_open_score_path),
        "handler_suite": _path_status(handler_suite_path),
        "handler_score": _path_status(handler_score_path),
        "gcp_probe": _path_status(gcp_probe_path),
    }
    expected_proof_paths = {
        "results_json": f"{proof_dir}/telegram-visible-direct-claim-suite-results-current.json",
        "score_json": f"{proof_dir}/telegram-visible-direct-claim-suite-score-current.json",
        "score_markdown": f"{proof_dir}/telegram-visible-direct-claim-suite-score-current.md",
        "final_screenshot": f"{proof_dir}/telegram-visible-direct-claim-suite-final.png",
        "final_accessibility": f"{proof_dir}/telegram-visible-direct-claim-suite-final-accessibility.txt",
        "db_before_after": f"{proof_dir}/telegram-visible-direct-claim-suite-db-before-after.json",
        "service_readback": f"{proof_dir}/telegram-visible-direct-claim-suite-service-readback.txt",
        "capture_receipt_json": str(DEFAULT_CAPTURE_RECEIPT),
        "capture_receipt_markdown": str(DEFAULT_CAPTURE_RECEIPT_MARKDOWN),
        "capture_results_json": str(DEFAULT_CAPTURE_RESULTS),
    }

    checks = {
        "live_open_telegram_suite_passed": _live_open_score_passes(live_open_score),
        "handler_direct_claim_suite_safe": _handler_suite_is_safe(handler_suite, expected_count=len(messages)),
        "handler_direct_claim_score_passed": _score_passes(handler_score, expected_count=len(messages)),
        "all_evidence_paths_exist": all(item["exists"] and item["bytes"] > 0 for item in evidence_paths.values()),
        "raw_dc_messages_preserved": [item["message"] for item in messages]
        == [prompt["message"] for prompt in benchmark.M3TAVERSAL_DIRECT_CLAIM_FOLLOWUP_SCENARIOS],
        "telegram_visible_messages_not_sent_by_packet": True,
        "production_apply_not_authorized_by_packet": True,
        "gcp_parity_not_required_for_vps_telegram_test": gcp_probe.get("ready_for_gcp_readonly_parity") is not True,
    }
    ready_to_request = all(checks.values())
    authorization_text = (
        "I authorize a Telegram-visible read-only direct-claim benchmark in the Leo group "
        f"`{chat_label}` (`{chat_id}`) using exactly the six raw messages DC-01 through DC-06 listed in "
        "`telegram-visible-direct-claim-authorization-packet-current.json`. Capture before/after DB counts, "
        "Telegram-visible replies, scorer output, final screenshot/accessibility proof, and service readback. "
        "Do not run production DB apply, do not edit SOUL.md, do not post any extra messages beyond those six "
        "benchmark prompts unless separately authorized."
    )

    return {
        "generated_at_utc": datetime.now(timezone.utc).isoformat(),
        "mode": "telegram_visible_direct_claim_authorization_packet_not_sent",
        "packet_generation_git_sha": sha,
        "ready_to_request_authorization": ready_to_request,
        "authorization_gate_status": "ready_to_request_explicit_authorization" if ready_to_request else "not_ready",
        "telegram_visible_messages_sent": False,
        "telegram_visible_message_authorization_present": False,
        "production_apply_executed": False,
        "production_apply_authorization_present": False,
        "mutates_db": False,
        "target": {
            "platform": "telegram",
            "chat_label": chat_label,
            "chat_id": chat_id,
            "message_count": len(messages),
            "expected_prompt_ids": expected_ids,
        },
        "exact_messages": messages,
        "checks": checks,
        "evidence_paths": evidence_paths,
        "expected_proof_paths_after_authorized_run": expected_proof_paths,
        "explicit_operator_authorization_text": authorization_text,
        "clear_CTA": (
            "To run the Telegram-visible DC benchmark, reply with the exact authorization text above. After that, "
            "Codex should send only the six listed messages to the Leo group, capture the retained proof paths, "
            "score the retained replies with `scripts/working_leo_open_ended_benchmark.py --results-json ... "
            "--include-direct-claim-followups`, and read back DB/service stability."
        ),
        "next_non_user_action_after_authorization": [
            "Read current service state and DB counts before sending.",
            "Send exactly the six raw DC-01..DC-06 messages to Telegram group Leo.",
            "Capture each Telegram-visible Leo reply plus a final screenshot/accessibility readback.",
            "Read DB counts and service state after sending.",
            "Assemble the retained capture receipt and score it before updating the current truth index.",
        ],
        "do_not_run_without": [
            "Exact operator authorization text naming the Telegram group and six-message scope.",
            "Authenticated Telegram UI/API route that can send to the Leo group.",
            "Before-count and after-count readbacks for public.* and kb_stage.kb_proposals.",
        ],
        "claim_ceiling": (
            "This packet makes the Telegram-visible DC benchmark executable and auditable. It does not send "
            "Telegram messages, does not mutate the DB, does not authorize production apply, and does not prove the "
            "DC prompts are Telegram-visible until the authorized run is actually completed and scored."
        ),
    }


def write_json(path: Path, data: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")


def write_markdown(path: Path, data: dict[str, Any]) -> None:
    lines = [
        "# Telegram-Visible Direct-Claim Authorization Packet",
        "",
        f"Generated UTC: `{data['generated_at_utc']}`",
        f"Mode: `{data['mode']}`",
        f"Ready to request authorization: `{data['ready_to_request_authorization']}`",
        f"Authorization gate status: `{data['authorization_gate_status']}`",
        f"Telegram-visible messages sent: `{data['telegram_visible_messages_sent']}`",
        f"Telegram-visible authorization present: `{data['telegram_visible_message_authorization_present']}`",
        f"Production apply executed: `{data['production_apply_executed']}`",
        f"Mutates DB: `{data['mutates_db']}`",
        "",
        "## Target",
        "",
        f"- Chat: `{data['target']['chat_label']}` (`{data['target']['chat_id']}`)",
        f"- Message count: `{data['target']['message_count']}`",
        f"- Prompt IDs: `{', '.join(data['target']['expected_prompt_ids'])}`",
        "",
        "## Exact Messages",
        "",
    ]
    for item in data["exact_messages"]:
        lines.extend(
            [
                f"### {item['prompt_id']} / {item['dimension']}",
                "",
                item["message"],
                "",
                f"- Required signals: `{', '.join(item['required_signals'])}`",
                "",
            ]
        )
    lines.extend(["## Checks", ""])
    for key, value in sorted(data["checks"].items()):
        lines.append(f"- `{key}`: `{value}`")
    lines.extend(
        [
            "",
            "## Explicit Operator Authorization Text",
            "",
            data["explicit_operator_authorization_text"],
            "",
            "## Clear CTA",
            "",
            data["clear_CTA"],
            "",
            "## Next Non-User Action After Authorization",
            "",
        ]
    )
    lines.extend(f"- {item}" for item in data["next_non_user_action_after_authorization"])
    lines.extend(["", "## Do Not Run Without", ""])
    lines.extend(f"- {item}" for item in data["do_not_run_without"])
    lines.extend(["", "## Claim Ceiling", "", data["claim_ceiling"], ""])
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text("\n".join(lines), encoding="utf-8")


def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--live-open-score", type=Path, default=DEFAULT_LIVE_OPEN_SCORE)
    parser.add_argument("--handler-suite", type=Path, default=DEFAULT_HANDLER_SUITE)
    parser.add_argument("--handler-score", type=Path, default=DEFAULT_HANDLER_SCORE)
    parser.add_argument("--gcp-probe", type=Path, default=DEFAULT_GCP_PROBE)
    parser.add_argument("--chat-id", default=DEFAULT_CHAT_ID)
    parser.add_argument("--chat-label", default=DEFAULT_CHAT_LABEL)
    parser.add_argument("--proof-dir", default=DEFAULT_PROOF_DIR)
    parser.add_argument("--out", type=Path, default=DEFAULT_OUT)
    parser.add_argument("--markdown-out", type=Path, default=DEFAULT_MARKDOWN_OUT)
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> int:
    args = parse_args(argv)
    data = build_packet(
        live_open_score_path=args.live_open_score,
        handler_suite_path=args.handler_suite,
        handler_score_path=args.handler_score,
        gcp_probe_path=args.gcp_probe,
        chat_id=args.chat_id,
        chat_label=args.chat_label,
        proof_dir=args.proof_dir,
    )
    write_json(args.out, data)
    write_markdown(args.markdown_out, data)
    print(
        json.dumps(
            {
                "out": str(args.out),
                "markdown_out": str(args.markdown_out),
                "ready_to_request_authorization": data["ready_to_request_authorization"],
                "telegram_visible_messages_sent": data["telegram_visible_messages_sent"],
                "authorization_gate_status": data["authorization_gate_status"],
            },
            indent=2,
            sort_keys=True,
        )
    )
    return 0 if data["ready_to_request_authorization"] else 2


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