#!/usr/bin/env python3
"""
IwoooS Public Gateway live conf 匯出請求包產生器。

本工具讀取 public gateway preflight inventory，產生 owner-provided live
conf 匯出請求草稿。它不 SSH、不讀 live conf、不保存 raw conf、不執行
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))

EXPORT_REQUEST_FIELDS = [
    "export_request_id",
    "config_id",
    "host",
    "live_path",
    "export_owner_role_or_team",
    "export_method",
    "redaction_policy_ref",
    "redacted_live_conf_ref",
    "source_snapshot_ref",
    "intended_use",
    "followup_owner",
    "not_approval",
]

REDACTION_RULES = [
    "只收 owner 提供的脫敏 live conf export ref，不收 raw live conf。",
    "不得包含 TLS private key、token、secret、cookie、session、authorization header 或 Basic Auth credential。",
    "若 upstream URL 含 credential，必須整段遮罩為 redacted_upstream_credential。",
    "若路徑含 private credential、query token 或 webhook secret，必須整段遮罩。",
    "允許保留 server_name、listen、location、proxy_pass host / port、ACME path、TLS certificate path metadata。",
    "不得貼主機 shell history、完整環境變數、私鑰內容、DB URL 或未脫敏 log。",
    "疑似敏感 payload 只能記 quarantine metadata，不得寫入 repo、LOGBOOK 或前端。",
    "匯出請求不等於 nginx -t、reload、route smoke、DNS / TLS probe 或 production write 授權。",
]


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 export_request_for(row: dict[str, Any]) -> dict[str, Any]:
    config_id = row["config_id"]
    return {
        "export_request_id": f"public_gateway_live_conf_export:{config_id}",
        "status": "draft_not_dispatched",
        "config_id": config_id,
        "host": row["host"],
        "role": row["role"],
        "control_tier": row["control_tier"],
        "owner_gate": row["owner_gate"],
        "repo_source_path": row["repo_source_path"],
        "live_path": row["live_path"],
        "export_owner_role_or_team": "pending_owner_role_or_team",
        "export_method": "owner_provided_redacted_export_only",
        "redaction_policy_ref": "docs/security/PUBLIC-GATEWAY-LIVE-CONF-EXPORT-REQUEST.md#3-redaction-policy",
        "redacted_live_conf_ref": None,
        "source_snapshot_ref": "docs/security/public-gateway-preflight-inventory.snapshot.json",
        "intended_use": "rendered_diff_and_route_change_preflight_only",
        "followup_owner": "pending_followup_owner",
        "not_approval": True,
        "export_request_fields": EXPORT_REQUEST_FIELDS,
        "redaction_rules": REDACTION_RULES,
        "route_impact_summary": {
            "server_name_count": row.get("server_name_count", 0),
            "upstream_count": row.get("upstream_count", 0),
            "tls_certificate_path_count": row.get("tls_certificate_path_count", 0),
            "admin_route_count": row.get("admin_route_count", 0),
            "websocket_route_count": row.get("websocket_route_count", 0),
            "acme_route_count": row.get("acme_route_count", 0),
        },
        "request_sent": False,
        "recipient_confirmed": False,
        "redacted_export_received": False,
        "raw_live_conf_stored": False,
        "rendered_diff_ready": False,
        "nginx_test_authorized": False,
        "nginx_test_executed": False,
        "nginx_reload_authorized": False,
        "route_smoke_authorized": False,
        "route_smoke_executed": False,
        "runtime_gate": False,
        "action_buttons_allowed": False,
        "secret_value_collection_allowed": False,
        "production_write_authorized": False,
    }


def build_report(root: Path, preflight_report: dict[str, Any], generated_at: str | None) -> dict[str, Any]:
    report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
    rows = preflight_report.get("config_preflight_rows", [])
    requests = [export_request_for(row) for row in rows]
    c0_requests = [request for request in requests if request.get("control_tier") == "C0"]
    c1_requests = [request for request in requests if request.get("control_tier") == "C1"]

    return {
        "schema_version": "public_gateway_live_conf_export_request_v1",
        "generated_at": report_time,
        "git_commit": git_short_sha(root),
        "source_preflight_schema_version": preflight_report.get("schema_version"),
        "source_preflight_status": preflight_report.get("status"),
        "status": "live_conf_export_request_ready_not_dispatched",
        "summary": {
            "export_request_count": len(requests),
            "c0_export_request_count": len(c0_requests),
            "c1_export_request_count": len(c1_requests),
            "export_request_field_count": len(EXPORT_REQUEST_FIELDS),
            "redaction_rule_count": len(REDACTION_RULES),
            "request_sent_count": 0,
            "recipient_confirmed_count": 0,
            "redacted_export_received_count": 0,
            "raw_live_conf_stored_count": 0,
            "rendered_diff_ready_count": 0,
            "nginx_test_authorized_count": 0,
            "nginx_test_executed_count": 0,
            "nginx_reload_authorized_count": 0,
            "route_smoke_authorized_count": 0,
            "route_smoke_executed_count": 0,
            "runtime_gate_count": 0,
            "action_button_count": 0,
        },
        "execution_boundaries": {
            "ssh_read_authorized": False,
            "host_live_conf_read_authorized": False,
            "raw_live_conf_storage_allowed": 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_query_executed": False,
            "live_tls_probe_executed": False,
            "certbot_renew_authorized": False,
            "runtime_execution_authorized": False,
            "action_buttons_allowed": False,
            "secret_value_collection_allowed": False,
            "production_write_authorized": False,
            "not_authorization": True,
        },
        "export_request_fields": EXPORT_REQUEST_FIELDS,
        "redaction_rules": REDACTION_RULES,
        "export_requests": requests,
        "next_steps": [
            "若 owner 願意提供，只能提供脫敏 live conf export ref，不得提供 raw conf。",
            "收到 export ref 後先做敏感 payload 隔離檢查，再進 rendered diff。",
            "rendered diff 成立仍不代表 nginx -t、reload 或 route smoke 已授權。",
        ],
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="IwoooS Public Gateway live conf 匯出請求包產生器")
    parser.add_argument("--root", default=".", help="repo root")
    parser.add_argument(
        "--preflight-report",
        default="docs/security/public-gateway-preflight-inventory.snapshot.json",
        help="public-gateway-preflight-inventory.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()
    preflight_report = load_json(root / args.preflight_report)
    report = build_report(root, preflight_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_LIVE_CONF_EXPORT_REQUEST_OK "
        f"requests={summary['export_request_count']} "
        f"c0={summary['c0_export_request_count']} "
        f"redaction_rules={summary['redaction_rule_count']} "
        f"received={summary['redacted_export_received_count']} "
        f"runtime_gate={summary['runtime_gate_count']}",
        file=sys.stderr,
    )
    return 0


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