#!/usr/bin/env python3
"""
IwoooS 高價值配置 owner packet 收件預檢產生器。

本工具讀取 committed owner packet snapshot，產生 request dispatch 前的
只讀預檢與 reviewer intake lanes。它不送 request、不收回覆、不寫入
reviewer queue、不建立 action button，也不開啟 runtime gate。
"""

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))

DISPATCH_PREFLIGHT_CHECKS = [
    (
        "baseline_commit_synced",
        "基線 commit 已同步",
        "使用最新 gitea/main 與 owner packet snapshot；不得用過期 packet 送件。",
    ),
    (
        "source_snapshot_current",
        "來源 snapshot 目前有效",
        "來源必須是 committed high-value-config-owner-packet snapshot，不讀 live 主機。",
    ),
    (
        "packet_scope_mapped",
        "Packet scope 已映射",
        "每包需有 category、priority、control tier、required gate 與 affected files。",
    ),
    (
        "canonical_fields_present",
        "Canonical 欄位已固定",
        "每包需要求 owner role、decision、reason、scope、evidence refs、followup、rollback、window、validation。",
    ),
    (
        "redacted_evidence_refs_only",
        "Evidence refs 只允許脫敏參照",
        "只收文件路徑、snapshot id、ticket id、commit、hash 或 metadata pointer。",
    ),
    (
        "no_sensitive_payload",
        "禁止敏感 payload",
        "不得收 token、secret、private key、cookie、session、authorization header、runner token 或 webhook secret。",
    ),
    (
        "no_execution_request",
        "拒收執行要求",
        "不得夾帶 reload、deploy、sync、host write、active scan、payout 或 withdrawal。",
    ),
    (
        "maintenance_and_rollback_fields_present",
        "維護窗口與回滾 owner 欄位存在",
        "C0 / P0 仍需獨立人工批准、維護窗口與 rollback owner，不能由 packet 自動授權。",
    ),
    (
        "validation_plan_present",
        "驗證計畫欄位存在",
        "必須能說明 preflight、post-check、rollback check；只讀文件變更則對應 guard / JSON / doc sanity。",
    ),
]

REVIEWER_INTAKE_LANES = [
    (
        "keep_waiting_owner_response",
        "尚未收到 owner response 或只有空白 / 口頭同意時維持等待。",
        "request / received / accepted / rejected 維持 0。",
    ),
    (
        "request_more_evidence",
        "欄位缺漏、scope 不清或 redacted evidence refs 不足時補件。",
        "不得增加 accepted 或 runtime gate。",
    ),
    (
        "quarantine_sensitive_payload",
        "疑似含敏感 payload、raw log、未脫敏截圖或 private credential URL 時隔離。",
        "不得保存 raw payload、不得渲染到前端或 LOGBOOK。",
    ),
    (
        "reject_execution_request",
        "夾帶 repo / refs / workflow / runner / host / scan / runtime 執行要求時拒收。",
        "不得建立 action button、不得轉成執行批准。",
    ),
    (
        "ready_for_reviewer_validation",
        "欄位完整、證據脫敏、無敏感 payload 且無執行要求時才可進 reviewer checklist。",
        "仍不是 accepted，也不是 runtime authorization。",
    ),
]

NEXT_STEPS = [
    "先由 reviewer 確認預檢 snapshot 與 owner packet snapshot 皆為最新 committed evidence。",
    "送件前只準備脫敏欄位與禁止條款，不送 secret value、不附 raw payload。",
    "收到回覆後先依 intake lanes 分流；通過 reviewer validation 之前 received / accepted 不得增加。",
    "任何 runtime、Nginx reload、DNS / TLS、workflow、runner、host 或 payout 動作都必須切獨立人工批准。",
]


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 dispatch_preflight_checks() -> list[dict[str, Any]]:
    return [
        {
            "check_id": check_id,
            "label": label,
            "status": "preflight_required_before_request_dispatch",
            "required": True,
            "instruction": instruction,
            "gate_effect": "不增加 request_sent / received / accepted / runtime gate。",
        }
        for check_id, label, instruction in DISPATCH_PREFLIGHT_CHECKS
    ]


def reviewer_intake_lanes() -> list[dict[str, Any]]:
    return [
        {
            "lane_id": lane_id,
            "status": "reviewer_intake_lane_defined",
            "instruction": instruction,
            "gate_effect": gate_effect,
        }
        for lane_id, instruction, gate_effect in REVIEWER_INTAKE_LANES
    ]


def intake_packet(packet: dict[str, Any]) -> dict[str, Any]:
    required_owner_fields = [
        field["field"]
        for field in packet.get("field_templates", [])
        if field.get("required") is True
    ]
    return {
        "packet_id": packet["packet_id"],
        "status": "waiting_request_dispatch_preflight",
        "category_id": packet["category_id"],
        "label": packet["label"],
        "priority": packet["priority"],
        "control_tier": packet["control_tier"],
        "required_gate": packet["required_gate"],
        "affected_file_count": len(packet.get("affected_files", [])),
        "affected_files": packet.get("affected_files", []),
        "required_owner_fields": required_owner_fields,
        "required_validation": packet.get("required_validation", []),
        "request_sent": False,
        "received_response": False,
        "accepted_response": False,
        "rejected_response": False,
        "reviewer_queue_write": False,
        "runtime_gate": False,
        "action_buttons_allowed": False,
        "secret_value_collection_allowed": False,
        "production_write_authorized": False,
        "not_authorization": True,
    }


def build_report(root: Path, owner_packet_report: dict[str, Any], generated_at: str | None) -> dict[str, Any]:
    report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
    packets = owner_packet_report.get("packets", [])
    c0_packets = [packet for packet in packets if packet.get("control_tier") == "C0"]
    c1_packets = [packet for packet in packets if packet.get("control_tier") == "C1"]
    canonical_owner_fields = owner_packet_report.get("canonical_owner_fields", [])
    required_owner_field_total = len(canonical_owner_fields) * len(packets)
    blocked_requests = packets[0].get("blocked_requests", []) if packets else []
    false_flags = owner_packet_report.get("universal_owner_response_template", {}).get("false_flags", {})

    return {
        "schema_version": "high_value_config_owner_packet_intake_preflight_v1",
        "generated_at": report_time,
        "git_commit": git_short_sha(root),
        "source_owner_packet_schema_version": owner_packet_report.get("schema_version"),
        "source_owner_packet_status": owner_packet_report.get("status"),
        "status": "request_dispatch_preflight_ready",
        "summary": {
            "packet_count": len(packets),
            "c0_packet_count": len(c0_packets),
            "c1_packet_count": len(c1_packets),
            "canonical_owner_field_count": len(canonical_owner_fields),
            "required_owner_field_total": required_owner_field_total,
            "dispatch_preflight_check_count": len(DISPATCH_PREFLIGHT_CHECKS),
            "reviewer_intake_lane_count": len(REVIEWER_INTAKE_LANES),
            "blocked_request_count": len(blocked_requests),
            "request_sent_count": 0,
            "received_response_count": 0,
            "accepted_response_count": 0,
            "rejected_response_count": 0,
            "reviewer_queue_write_count": 0,
            "runtime_gate_count": 0,
            "action_button_count": 0,
        },
        "execution_boundaries": {
            "dispatch_authorized": False,
            "request_sent": False,
            "reviewer_queue_write": False,
            "runtime_execution_authorized": False,
            "secret_value_collection_allowed": False,
            "production_write_authorized": False,
            "host_write_authorized": False,
            "nginx_reload_authorized": False,
            "dns_tls_change_authorized": False,
            "workflow_modification_authorized": False,
            "active_scan_authorized": False,
            "action_buttons_allowed": False,
            "not_authorization": True,
        },
        "canonical_owner_fields": canonical_owner_fields,
        "allowed_decisions": owner_packet_report.get("allowed_decisions", []),
        "dispatch_preflight_checks": dispatch_preflight_checks(),
        "reviewer_intake_lanes": reviewer_intake_lanes(),
        "blocked_requests": blocked_requests,
        "source_false_flags": false_flags,
        "intake_packets": [intake_packet(packet) for packet in packets],
        "completion": {
            "request_dispatch_preflight_artifact_percent": 100,
            "owner_request_sent_percent": 0,
            "owner_response_received_percent": 0,
            "reviewer_accepted_percent": 0,
            "runtime_gate_percent": 0,
        },
        "next_steps": NEXT_STEPS,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="IwoooS 高價值配置 owner packet 收件預檢產生器")
    parser.add_argument("--root", default=".", help="repo root")
    parser.add_argument(
        "--owner-packet-report",
        default="docs/security/high-value-config-owner-packet.snapshot.json",
        help="high-value-config-owner-packet.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()
    owner_packet_report = load_json(root / args.owner_packet_report)
    report = build_report(root, owner_packet_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(
        "HIGH_VALUE_CONFIG_OWNER_PACKET_INTAKE_PREFLIGHT_OK "
        f"packets={summary['packet_count']} "
        f"c0={summary['c0_packet_count']} "
        f"checks={summary['dispatch_preflight_check_count']} "
        f"lanes={summary['reviewer_intake_lane_count']} "
        f"runtime_gate={summary['runtime_gate_count']}",
        file=sys.stderr,
    )
    return 0


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