#!/usr/bin/env python3
"""
IwoooS Public Gateway rendered diff gate 草稿產生器。

本工具讀取 redacted export intake preflight snapshot，產生未來 rendered
diff、nginx -t、reload 與 route smoke 的分階段 gate 草稿。它不讀 live
conf、不產生 diff、不執行 nginx -t、不 reload、不做 route smoke。
"""

from __future__ import annotations

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


TAIPEI = timezone(timedelta(hours=8))

DIFF_GATE_FIELDS = [
    "diff_gate_id",
    "intake_id",
    "export_request_id",
    "config_id",
    "control_tier",
    "source_config_ref",
    "redacted_live_conf_ref",
    "rendered_diff_ref",
    "nginx_test_plan_ref",
    "route_smoke_plan_ref",
    "rollback_owner",
    "not_approval",
]

PREFLIGHT_STAGES = [
    (
        "redacted_export_acceptance_required",
        "必須先有合格 redacted export accepted metadata，否則不得產生 rendered diff。",
    ),
    (
        "normalize_without_raw_conf_storage",
        "只可在隔離工作區以脫敏 ref 產生 normalized diff，不得把 raw live conf 寫入 repo。",
    ),
    (
        "rendered_diff_owner_review_required",
        "rendered diff 只可成為 owner review candidate，不自動批准。",
    ),
    (
        "nginx_test_approval_package_required",
        "`nginx -t` 必須另有人工批准包、rollback owner 與維護窗口。",
    ),
    (
        "reload_approval_separate",
        "reload 與 public route change 必須獨立於 rendered diff 與 nginx -t。",
    ),
    (
        "route_smoke_matrix_required",
        "route smoke 需列出 affected routes、預期 status、TLS / WebSocket / ACME checks。",
    ),
    (
        "postcheck_and_rollback_required",
        "任何未來執行前都需 rollback owner、post-check 與失敗撤回條件。",
    ),
]

BLOCKED_ACTIONS = [
    "read_live_conf_over_ssh",
    "store_raw_live_conf",
    "render_diff_from_unredacted_payload",
    "nginx_test_without_approval",
    "nginx_reload_without_approval",
    "route_smoke_without_plan",
    "dns_probe_without_approval",
    "tls_probe_without_approval",
    "certbot_renew_without_approval",
    "modify_nginx_conf",
    "modify_dns_tls_config",
    "change_public_route",
    "write_production_host",
    "open_runtime_gate",
]


def git_short_sha(root: Path) -> str:
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--short", "HEAD"],
            cwd=root,
            check=True,
            capture_output=True,
            text=True,
        )
        return result.stdout.strip()
    except Exception:
        return "unknown"


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


def preflight_stages() -> list[dict[str, Any]]:
    return [
        {
            "stage_id": stage_id,
            "status": "required_before_runtime_action",
            "instruction": instruction,
            "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。",
        }
        for stage_id, instruction in PREFLIGHT_STAGES
    ]


def diff_gate_candidate(intake: dict[str, Any]) -> dict[str, Any]:
    config_id = intake["config_id"]
    return {
        "diff_gate_id": f"public_gateway_rendered_diff_gate:{config_id}",
        "status": "draft_waiting_redacted_export_acceptance",
        "intake_id": intake["intake_id"],
        "export_request_id": intake["export_request_id"],
        "config_id": config_id,
        "host": intake["host"],
        "live_path": intake["live_path"],
        "control_tier": intake["control_tier"],
        "owner_gate": intake["owner_gate"],
        "source_config_ref": "docs/security/public-gateway-preflight-inventory.snapshot.json",
        "redacted_live_conf_ref": None,
        "rendered_diff_ref": None,
        "nginx_test_plan_ref": None,
        "route_smoke_plan_ref": None,
        "rollback_owner": "pending_rollback_owner",
        "diff_gate_fields": DIFF_GATE_FIELDS,
        "preflight_stages": [stage_id for stage_id, _instruction in PREFLIGHT_STAGES],
        "blocked_actions": BLOCKED_ACTIONS,
        "not_approval": True,
        "redacted_export_accepted": False,
        "rendered_diff_candidate": False,
        "rendered_diff_ready": False,
        "nginx_test_authorized": False,
        "nginx_test_executed": False,
        "nginx_reload_authorized": False,
        "nginx_reload_executed": False,
        "route_smoke_authorized": False,
        "route_smoke_executed": False,
        "dns_tls_probe_authorized": False,
        "certbot_renew_authorized": False,
        "maintenance_window_accepted": False,
        "rollback_owner_accepted": False,
        "runtime_gate": False,
        "action_buttons_allowed": False,
        "production_write_authorized": False,
    }


def build_report(root: Path, intake_report: dict[str, Any], generated_at: str | None) -> dict[str, Any]:
    report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
    intake_candidates = intake_report.get("intake_candidates", [])
    diff_gate_candidates = [diff_gate_candidate(item) for item in intake_candidates]
    c0_candidates = [item for item in diff_gate_candidates if item.get("control_tier") == "C0"]
    c1_candidates = [item for item in diff_gate_candidates if item.get("control_tier") == "C1"]

    return {
        "schema_version": "public_gateway_rendered_diff_gate_draft_v1",
        "generated_at": report_time,
        "git_commit": git_short_sha(root),
        "source_intake_preflight_schema_version": intake_report.get("schema_version"),
        "source_intake_preflight_status": intake_report.get("status"),
        "status": "rendered_diff_gate_draft_ready_no_runtime_action",
        "summary": {
            "diff_gate_candidate_count": len(diff_gate_candidates),
            "c0_diff_gate_candidate_count": len(c0_candidates),
            "c1_diff_gate_candidate_count": len(c1_candidates),
            "diff_gate_field_count": len(DIFF_GATE_FIELDS),
            "preflight_stage_count": len(PREFLIGHT_STAGES),
            "blocked_action_count": len(BLOCKED_ACTIONS),
            "redacted_export_accepted_count": 0,
            "rendered_diff_candidate_count": 0,
            "rendered_diff_ready_count": 0,
            "nginx_test_authorized_count": 0,
            "nginx_test_executed_count": 0,
            "nginx_reload_authorized_count": 0,
            "nginx_reload_executed_count": 0,
            "route_smoke_authorized_count": 0,
            "route_smoke_executed_count": 0,
            "dns_tls_probe_authorized_count": 0,
            "certbot_renew_authorized_count": 0,
            "maintenance_window_accepted_count": 0,
            "rollback_owner_accepted_count": 0,
            "runtime_gate_count": 0,
            "action_button_count": 0,
        },
        "execution_boundaries": {
            "read_live_conf_over_ssh": False,
            "store_raw_live_conf": False,
            "rendered_diff_authorized": False,
            "nginx_test_authorized": False,
            "nginx_test_executed": False,
            "nginx_reload_authorized": False,
            "nginx_reload_executed": False,
            "route_smoke_authorized": False,
            "route_smoke_executed": False,
            "dns_tls_probe_authorized": False,
            "certbot_renew_authorized": False,
            "production_write_authorized": False,
            "runtime_execution_authorized": False,
            "action_buttons_allowed": False,
            "not_authorization": True,
        },
        "diff_gate_fields": DIFF_GATE_FIELDS,
        "preflight_stages": preflight_stages(),
        "blocked_actions": BLOCKED_ACTIONS,
        "diff_gate_candidates": diff_gate_candidates,
        "next_steps": [
            "等待 redacted export accepted metadata；沒有 accepted metadata 前不得產生 rendered diff。",
            "rendered diff candidate 必須另走 reviewer / owner review，不得自動進 nginx -t。",
            "`nginx -t`、reload、route smoke、DNS / TLS probe、certbot renew 與 production write 都必須另行人工批准。",
        ],
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="IwoooS Public Gateway rendered diff gate 草稿產生器")
    parser.add_argument("--root", default=".", help="repo root")
    parser.add_argument(
        "--intake-preflight-report",
        default="docs/security/public-gateway-redacted-export-intake-preflight.snapshot.json",
        help="public-gateway-redacted-export-intake-preflight.py 輸出的 JSON",
    )
    parser.add_argument("--output", help="寫出 JSON 報告")
    parser.add_argument("--generated-at", help="固定報告時間，供 committed snapshot 使用")
    args = parser.parse_args()

    root = Path(args.root).resolve()
    intake_report = load_json(root / args.intake_preflight_report)
    report = build_report(root, intake_report, args.generated_at)
    payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)

    if args.output:
        output = Path(args.output)
        output.parent.mkdir(parents=True, exist_ok=True)
        output.write_text(payload + "\n", encoding="utf-8")
    else:
        print(payload)

    summary = report["summary"]
    print(
        "PUBLIC_GATEWAY_RENDERED_DIFF_GATE_DRAFT_OK "
        f"candidates={summary['diff_gate_candidate_count']} "
        f"c0={summary['c0_diff_gate_candidate_count']} "
        f"stages={summary['preflight_stage_count']} "
        f"blocked={summary['blocked_action_count']} "
        f"runtime_gate={summary['runtime_gate_count']}",
        file=sys.stderr,
    )
    return 0


if __name__ == "__main__":
    sys.exit(main())
