#!/usr/bin/env python3
"""Build a guarded Working Leo authorized-apply session plan.

This is not an executor. It converts the authorized apply runbook into an
operator session plan with output paths for every preflight, apply, postflight,
regression, service readback, cleanup readback, and receipt-validation step.
The goal is to make an authorized apply window auditable before it starts.
"""

from __future__ import annotations

import argparse
import json
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_RECEIPT_TEMPLATE = REPORT_DIR / "working-leo-production-apply-receipt-template-current.json"
DEFAULT_OUT = REPORT_DIR / "working-leo-authorized-apply-session-plan-current.json"
DEFAULT_MARKDOWN_OUT = REPORT_DIR / "working-leo-authorized-apply-session-plan-current.md"
DEFAULT_OUTPUT_DIR = REPORT_DIR / "production-apply-session-artifacts"


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


def _command_with_output(base_command: str, output_path: Path) -> str:
    return f"mkdir -p {output_path.parent} && {base_command} | tee {output_path}"


def _phase_steps(runbook: dict[str, Any], phase_key: str, output_dir: Path) -> list[dict[str, Any]]:
    steps: list[dict[str, Any]] = []
    for index, entry in enumerate(runbook["phases"][phase_key], start=1):
        packet_id = entry["packet_id"]
        phase = entry["phase"]
        output_path = output_dir / f"{index:02d}-{phase}-{packet_id}.log"
        steps.append(
            {
                "index": index,
                "id": f"{phase}:{packet_id}",
                "phase": phase,
                "packet_id": packet_id,
                "source_sql": entry["path"],
                "output_path": str(output_path),
                "command": _command_with_output(entry["command_template"], output_path),
                "mutates_production_db": phase in {"apply", "rollback"},
                "requires_authorization": phase in {"apply", "rollback"},
            }
        )
    return steps


def _pre_apply_steps(runbook: dict[str, Any], output_dir: Path) -> list[dict[str, Any]]:
    steps: list[dict[str, Any]] = []
    for index, command in enumerate(runbook.get("pre_apply_validation_commands", []), start=1):
        output_path = output_dir / f"pre-apply-validation-{index:02d}-{command['id']}.log"
        steps.append(
            {
                "index": index,
                "id": command["id"],
                "phase": "pre_apply_validation",
                "output_path": str(output_path),
                "command": _command_with_output(command["command_template"], output_path),
                "expected_artifact": command["expected_artifact"],
                "mutates_production_db": bool(command.get("mutates_production_db")),
                "requires_authorization": False,
            }
        )
    return steps


def _post_apply_steps(runbook: dict[str, Any], output_dir: Path) -> list[dict[str, Any]]:
    steps: list[dict[str, Any]] = []
    for index, command in enumerate(runbook.get("post_apply_regression_commands", []), start=1):
        output_path = output_dir / f"post-apply-regression-{index:02d}-{command['id']}.log"
        steps.append(
            {
                "index": index,
                "id": command["id"],
                "phase": "post_apply_regression",
                "output_path": str(output_path),
                "command": _command_with_output(command["command_template"], output_path),
                "expected_artifact": command["expected_artifact"],
                "mutates_production_db": bool(command.get("mutates_production_db")),
                "posts_to_telegram": bool(command.get("posts_to_telegram")),
                "requires_authorization": False,
            }
        )
    return steps


def _receipt_fields_from_plan(plan: dict[str, Any]) -> dict[str, Any]:
    apply_outputs = [step["output_path"] for step in plan["apply_steps"]]
    postflight_outputs = [step["output_path"] for step in plan["postflight_steps"]]
    regression_outputs = [step["output_path"] for step in plan["post_apply_regression_steps"]]
    return {
        "preflight_output_paths": [step["output_path"] for step in plan["preflight_steps"]],
        "apply_output_paths": apply_outputs,
        "postflight_output_paths": postflight_outputs,
        "telegram_or_handler_regression_paths": regression_outputs,
        "service_readback": plan["service_readback_step"]["output_path"],
        "cleanup_readback": plan["cleanup_readback_step"]["output_path"],
        "rollback_packet_paths": [step["source_sql"] for step in plan["rollback_steps"]],
    }


def build_plan(
    *,
    runbook_path: Path = DEFAULT_RUNBOOK,
    receipt_template_path: Path = DEFAULT_RECEIPT_TEMPLATE,
    output_dir: Path = DEFAULT_OUTPUT_DIR,
) -> dict[str, Any]:
    runbook = _load_json(runbook_path)
    receipt_template = _load_json(receipt_template_path)

    pre_apply = _pre_apply_steps(runbook, output_dir)
    preflight = _phase_steps(runbook, "preflight_order", output_dir)
    apply = _phase_steps(runbook, "apply_order", output_dir)
    postflight = _phase_steps(runbook, "postflight_order", output_dir)
    rollback = _phase_steps(runbook, "rollback_order", output_dir)
    post_apply = _post_apply_steps(runbook, output_dir)
    service_readback = {
        "id": "service_readback",
        "phase": "post_apply_service_readback",
        "output_path": str(output_dir / "post-apply-service-readback.log"),
        "command": (
            "systemctl show leoclean-gateway.service "
            "-p ActiveState -p SubState -p MainPID -p NRestarts -p ExecMainStartTimestamp --no-pager "
            f"| tee {output_dir / 'post-apply-service-readback.log'}"
        ),
        "mutates_production_db": False,
        "requires_authorization": False,
    }
    cleanup_readback = {
        "id": "cleanup_readback",
        "phase": "post_apply_cleanup_readback",
        "output_path": str(output_dir / "post-apply-cleanup-readback.log"),
        "command": (
            "docker exec -i teleo-pg psql -U postgres -d teleo -At -q "
            "-c \"select datname from pg_database where datname ~ '(rehearsal|clone|staging)' order by datname\" "
            f"| tee {output_dir / 'post-apply-cleanup-readback.log'}"
        ),
        "mutates_production_db": False,
        "requires_authorization": False,
    }
    receipt_validation = {
        "id": "receipt_validation",
        "phase": "receipt_validation",
        "output_path": str(output_dir / "receipt-validation.log"),
        "command": (
            ".venv/bin/python scripts/validate_working_leo_production_receipt.py "
            "--receipt docs/reports/leo-working-state-20260709/working-leo-production-apply-receipt-current.json "
            f"| tee {output_dir / 'receipt-validation.log'}"
        ),
        "mutates_production_db": False,
        "requires_authorization": False,
    }

    plan: dict[str, Any] = {
        "generated_at_utc": datetime.now(timezone.utc).isoformat(),
        "mode": "authorized_apply_session_plan_not_executed",
        "runbook_path": str(runbook_path),
        "receipt_template_path": str(receipt_template_path),
        "output_dir": str(output_dir),
        "production_apply_executed": False,
        "authorization_required_before_apply_steps": True,
        "authorization_boundary": runbook["required_authorization_boundary"],
        "expected_after_apply_counts": runbook["expected_after_apply_counts"],
        "pre_apply_validation_steps": pre_apply,
        "preflight_steps": preflight,
        "apply_steps": apply,
        "postflight_steps": postflight,
        "post_apply_regression_steps": post_apply,
        "service_readback_step": service_readback,
        "cleanup_readback_step": cleanup_readback,
        "receipt_validation_step": receipt_validation,
        "rollback_steps": rollback,
        "receipt_template_must_fill_after_authorized_apply": receipt_template[
            "must_fill_after_authorized_apply"
        ],
        "claim_ceiling": (
            "This is an apply-session plan only. It does not execute SQL, does not authorize production apply, "
            "and does not prove canonical rows were changed."
        ),
    }
    plan["receipt_field_sources_after_execution"] = _receipt_fields_from_plan(plan)
    plan["all_apply_steps_require_authorization"] = all(step["requires_authorization"] for step in apply)
    plan["all_rollback_steps_require_authorization"] = all(step["requires_authorization"] for step in rollback)
    plan["non_apply_steps_do_not_mutate_db"] = all(
        not step.get("mutates_production_db")
        for group in (pre_apply, preflight, postflight, post_apply, [service_readback, cleanup_readback, receipt_validation])
        for step in group
    )
    return plan


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 Authorized Apply Session Plan",
        "",
        f"Generated UTC: `{data['generated_at_utc']}`",
        f"Mode: `{data['mode']}`",
        f"Production apply executed: `{data['production_apply_executed']}`",
        f"All apply steps require authorization: `{data['all_apply_steps_require_authorization']}`",
        f"All rollback steps require authorization: `{data['all_rollback_steps_require_authorization']}`",
        f"Non-apply steps do not mutate DB: `{data['non_apply_steps_do_not_mutate_db']}`",
        "",
        "## Authorization Boundary",
        "",
        data["authorization_boundary"],
        "",
    ]
    for title, key in [
        ("Pre-Apply Validation", "pre_apply_validation_steps"),
        ("Preflight", "preflight_steps"),
        ("Apply", "apply_steps"),
        ("Postflight", "postflight_steps"),
        ("Post-Apply Regression", "post_apply_regression_steps"),
        ("Rollback", "rollback_steps"),
    ]:
        lines.extend([f"## {title}", ""])
        for step in data[key]:
            lines.append(f"- `{step['id']}` -> `{step['output_path']}`")
            lines.append(f"  - mutates production DB: `{step.get('mutates_production_db', False)}`")
            lines.append(f"  - requires authorization: `{step.get('requires_authorization', False)}`")
    lines.extend(["## Receipt Field Sources After Execution", ""])
    for field, value in data["receipt_field_sources_after_execution"].items():
        lines.append(f"- `{field}`: `{value}`")
    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("--receipt-template", type=Path, default=DEFAULT_RECEIPT_TEMPLATE)
    parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT_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_plan(
        runbook_path=args.runbook,
        receipt_template_path=args.receipt_template,
        output_dir=args.output_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),
                "production_apply_executed": data["production_apply_executed"],
                "all_apply_steps_require_authorization": data["all_apply_steps_require_authorization"],
                "all_rollback_steps_require_authorization": data["all_rollback_steps_require_authorization"],
                "non_apply_steps_do_not_mutate_db": data["non_apply_steps_do_not_mutate_db"],
            },
            indent=2,
            sort_keys=True,
        )
    )
    return 0


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