#!/usr/bin/env python3
"""Build the Working Leo production-apply authorization packet.

This is an operator packet, not an executor. It composes the retained runbook,
session plan, preflight baseline, direct-claim score, and receipt template into
one auditable authorization request. It never opens a database connection,
never runs SQL, and never authorizes production apply by itself.
"""

from __future__ import annotations

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

REPORT_DIR = Path("docs/reports/leo-working-state-20260709")
DEFAULT_RUNBOOK = REPORT_DIR / "working-leo-authorized-apply-runbook-current.json"
DEFAULT_SESSION_PLAN = REPORT_DIR / "working-leo-authorized-apply-session-plan-current.json"
DEFAULT_PREFLIGHT = REPORT_DIR / "working-leo-production-preflight-current.json"
DEFAULT_DIRECT_CLAIM_SCORE = REPORT_DIR / "telegram-handler-direct-claim-suite-score-current.json"
DEFAULT_RECEIPT_TEMPLATE = REPORT_DIR / "working-leo-production-apply-receipt-template-current.json"
DEFAULT_RECEIPT_VALIDATION = REPORT_DIR / "working-leo-production-apply-receipt-validation-current.json"
DEFAULT_OUT = REPORT_DIR / "working-leo-production-apply-authorization-packet-current.json"
DEFAULT_MARKDOWN_OUT = REPORT_DIR / "working-leo-production-apply-authorization-packet-current.md"


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 _apply_order(runbook: dict[str, Any]) -> list[str]:
    return [str(item["packet_id"]) for item in runbook["phases"]["apply_order"]]


def _expected_counts(runbook: dict[str, Any]) -> dict[str, Any]:
    return dict(runbook["expected_after_apply_counts"])


def _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") == 6
        and score_block.get("failures") == []
        and score_block.get("missing_prompt_ids") == []
        and score_block.get("unexpected_prompt_ids") == []
    )


def _template_has_required_after_fields(template: dict[str, Any], runbook: dict[str, Any]) -> bool:
    required = set(runbook["production_apply_receipt_required_fields"])
    receipt = template.get("receipt_template", {})
    must_fill = set(template.get("must_fill_after_authorized_apply", []))
    required_after = {
        "explicit_authorization_record",
        "operator_identity",
        "apply_output_paths",
        "postflight_output_paths",
        "actual_after_apply_counts",
        "telegram_or_handler_regression_paths",
        "service_readback",
        "cleanup_readback",
    }
    return required.issubset(receipt.keys()) and required_after.issubset(must_fill)


def build_packet(
    *,
    runbook_path: Path = DEFAULT_RUNBOOK,
    session_plan_path: Path = DEFAULT_SESSION_PLAN,
    preflight_path: Path = DEFAULT_PREFLIGHT,
    direct_claim_score_path: Path = DEFAULT_DIRECT_CLAIM_SCORE,
    receipt_template_path: Path = DEFAULT_RECEIPT_TEMPLATE,
    receipt_validation_path: Path = DEFAULT_RECEIPT_VALIDATION,
    git_sha: str | None = None,
) -> dict[str, Any]:
    runbook = _load_json(runbook_path)
    session_plan = _load_json(session_plan_path)
    preflight = _load_json(preflight_path)
    direct_claim_score = _load_json(direct_claim_score_path)
    receipt_template = _load_json(receipt_template_path)
    receipt_validation = _load_json(receipt_validation_path) if receipt_validation_path.exists() else {}
    sha = git_sha if git_sha is not None else current_git_sha()
    apply_order = _apply_order(runbook)

    evidence_paths = {
        "runbook": _path_status(runbook_path),
        "session_plan": _path_status(session_plan_path),
        "preflight": _path_status(preflight_path),
        "direct_claim_score": _path_status(direct_claim_score_path),
        "receipt_template": _path_status(receipt_template_path),
        "receipt_validation": _path_status(receipt_validation_path),
    }

    checks = {
        "runbook_ready_not_executed": runbook.get("ready_for_authorized_operator") is True
        and runbook.get("production_apply_executed") is False
        and runbook.get("production_apply_authorization_present") is False,
        "session_plan_not_executed_and_gated": session_plan.get("production_apply_executed") is False
        and session_plan.get("all_apply_steps_require_authorization") is True
        and session_plan.get("all_rollback_steps_require_authorization") is True
        and session_plan.get("non_apply_steps_do_not_mutate_db") is True,
        "read_only_preflight_passed": preflight.get("status") == "pass"
        and preflight.get("production_apply_executed") is False
        and preflight.get("mutates_production_db") is False
        and preflight.get("applies_production_packet") is False,
        "direct_claim_score_passed": _score_passes(direct_claim_score),
        "receipt_template_not_final_receipt": receipt_template.get("production_apply_executed") is False
        and receipt_template.get("valid_production_receipt") is False
        and _template_has_required_after_fields(receipt_template, runbook),
        "receipt_validator_still_missing_receipt": receipt_validation.get("status") == "missing_receipt"
        and receipt_validation.get("valid") is False,
        "all_evidence_paths_exist": all(item["exists"] and item["bytes"] > 0 for item in evidence_paths.values()),
    }
    ready_to_request = all(checks.values())

    authorization_sentence_template = (
        "I authorize production DB apply for the integrated Working Leo packet set on VPS using deployed commit "
        f"<DEPLOYED_SHA_READBACK> in this order: {', '.join(apply_order)}. Run preflight immediately before each "
        "phase, run the authorized apply SQL, run every postflight, run the non-posting direct-claim regression and "
        "scorer, capture service and cleanup readbacks, validate the production receipt, and do not post "
        "Telegram-visible messages unless separately authorized."
    )

    return {
        "generated_at_utc": datetime.now(timezone.utc).isoformat(),
        "mode": "production_apply_authorization_packet_not_executed",
        "production_apply_executed": False,
        "production_apply_authorization_present": False,
        "ready_to_request_authorization": ready_to_request,
        "authorization_gate_status": "ready_to_request_explicit_authorization" if ready_to_request else "not_ready",
        "packet_generation_git_sha": sha,
        "authorization_commit_sha_rule": "Use the fresh deployed SHA read back immediately before apply.",
        "authorization_scope": {
            "target": "VPS production Postgres canonical Working Leo packet apply",
            "packet_set": "integrated Working Leo packet set",
            "apply_order": apply_order,
            "expected_after_apply_counts": _expected_counts(runbook),
            "telegram_visible_post_authorized": False,
            "gcp_parity_in_scope": False,
        },
        "checks": checks,
        "evidence_paths": evidence_paths,
        "explicit_operator_authorization_text_template": authorization_sentence_template,
        "clear_CTA": (
            "If you want this production DB apply to run, first let Codex re-read the deployed SHA, then reply with "
            "the authorization sentence in `explicit_operator_authorization_text_template` after replacing "
            "`<DEPLOYED_SHA_READBACK>` with that SHA, plus the operator identity to record in the receipt."
        ),
        "next_non_user_action_after_authorization": [
            "Re-read deployed SHA, service state, and production preflight baseline.",
            "Run each packet phase in manifest order: preflight, authorized apply SQL, postflight.",
            "Run the non-posting direct-claim handler suite and strict scorer.",
            "Capture service readback, cleanup readback, and production receipt.",
            "Run the offline production receipt validator and retain the validation artifacts.",
        ],
        "do_not_run_without": [
            "Exact operator authorization text naming production DB apply scope.",
            "Operator identity for the production receipt.",
            "Fresh preflight immediately before apply.",
        ],
        "claim_ceiling": (
            "This packet requests explicit authorization and proves the request is bounded by retained evidence. It "
            "does not authorize production apply, does not run SQL, does not mutate production, and does not prove "
            "canonical rows were changed."
        ),
    }


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 = [
        "# Working Leo Production Apply Authorization Packet",
        "",
        f"Generated UTC: `{data['generated_at_utc']}`",
        f"Mode: `{data['mode']}`",
        f"Production apply executed: `{data['production_apply_executed']}`",
        f"Production apply authorization present: `{data['production_apply_authorization_present']}`",
        f"Ready to request authorization: `{data['ready_to_request_authorization']}`",
        f"Authorization gate status: `{data['authorization_gate_status']}`",
        f"Packet generation git SHA: `{data['packet_generation_git_sha']}`",
        f"Authorization commit SHA rule: `{data['authorization_commit_sha_rule']}`",
        "",
        "## Scope",
        "",
        f"- Target: `{data['authorization_scope']['target']}`",
        f"- Packet set: `{data['authorization_scope']['packet_set']}`",
        f"- Apply order: `{', '.join(data['authorization_scope']['apply_order'])}`",
        f"- Telegram-visible post authorized: `{data['authorization_scope']['telegram_visible_post_authorized']}`",
        f"- GCP parity in scope: `{data['authorization_scope']['gcp_parity_in_scope']}`",
        "",
        "## 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_template"],
            "",
            "## 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("--runbook", type=Path, default=DEFAULT_RUNBOOK)
    parser.add_argument("--session-plan", type=Path, default=DEFAULT_SESSION_PLAN)
    parser.add_argument("--preflight", type=Path, default=DEFAULT_PREFLIGHT)
    parser.add_argument("--direct-claim-score", type=Path, default=DEFAULT_DIRECT_CLAIM_SCORE)
    parser.add_argument("--receipt-template", type=Path, default=DEFAULT_RECEIPT_TEMPLATE)
    parser.add_argument("--receipt-validation", type=Path, default=DEFAULT_RECEIPT_VALIDATION)
    parser.add_argument("--out", type=Path, default=DEFAULT_OUT)
    parser.add_argument("--markdown-out", type=Path, default=DEFAULT_MARKDOWN_OUT)
    parser.add_argument("--git-sha", default=None)
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> int:
    args = parse_args(argv)
    data = build_packet(
        runbook_path=args.runbook,
        session_plan_path=args.session_plan,
        preflight_path=args.preflight,
        direct_claim_score_path=args.direct_claim_score,
        receipt_template_path=args.receipt_template,
        receipt_validation_path=args.receipt_validation,
        git_sha=args.git_sha,
    )
    write_json(args.out, data)
    write_markdown(args.markdown_out, data)
    print(
        json.dumps(
            {
                "out": str(args.out),
                "markdown_out": str(args.markdown_out),
                "authorization_gate_status": data["authorization_gate_status"],
                "production_apply_executed": data["production_apply_executed"],
                "ready_to_request_authorization": data["ready_to_request_authorization"],
            },
            indent=2,
            sort_keys=True,
        )
    )
    return 0 if data["ready_to_request_authorization"] else 1


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