#!/usr/bin/env python3
"""
IwoooS Wazuh 只讀 API release lane preflight。

本工具只檢查 repo 內 committed snapshot，固定 release 前必須由正式 lane
提供非敏感證據；不讀 git credential、不推送、不部署、不查 Wazuh、不改主機。
"""

from __future__ import annotations

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


TAIPEI = timezone(timedelta(hours=8))
SNAPSHOT_PATH = Path("docs/security/wazuh-readonly-release-lane-preflight.snapshot.json")
REQUIRED_ACK_FLAGS = [
    "approve_formal_release_lane",
    "confirm_no_plaintext_token_workaround",
    "confirm_no_force_push",
    "confirm_no_runtime_workaround",
    "confirm_production_readback_after_deploy",
    "confirm_wazuh_live_metadata_requires_separate_owner_gate",
]
REQUIRED_EVIDENCE_FIELDS = [
    "release_lane_owner",
    "release_method",
    "target_branch_or_patch_set",
    "post_deploy_readback_command",
    "rollback_owner",
    "blocked_runtime_actions_ack",
]
ALLOWED_RELEASE_METHODS = [
    "formal_gitea_merge",
    "formal_patch_apply",
    "maintainer_local_push_with_safe_credential",
]


def now_iso() -> str:
    return datetime.now(TAIPEI).replace(microsecond=0).isoformat()


def build_report(generated_at: str | None = None) -> dict[str, Any]:
    return {
        "schema_version": "iwooos_wazuh_readonly_release_lane_preflight_v1",
        "generated_at": generated_at or now_iso(),
        "status": "blocked_waiting_formal_release_lane_owner_response",
        "mode": "repo_preflight_no_secret_no_runtime_no_push",
        "required_ack_flags": REQUIRED_ACK_FLAGS,
        "required_evidence_fields": REQUIRED_EVIDENCE_FIELDS,
        "allowed_release_methods": ALLOWED_RELEASE_METHODS,
        "summary": {
            "required_ack_flag_count": len(REQUIRED_ACK_FLAGS),
            "accepted_ack_flag_count": 0,
            "required_evidence_field_count": len(REQUIRED_EVIDENCE_FIELDS),
            "accepted_evidence_field_count": 0,
            "allowed_release_method_count": len(ALLOWED_RELEASE_METHODS),
            "formal_release_lane_ready_count": 0,
            "safe_credential_available_count": 0,
            "patch_apply_authorized_count": 0,
            "gitea_push_authorized_count": 0,
            "production_deploy_authorized_count": 0,
            "production_readback_required_count": 1,
            "production_readback_passed_count": 0,
            "plain_text_token_workaround_allowed_count": 0,
            "force_push_allowed_count": 0,
            "runtime_workaround_allowed_count": 0,
            "wazuh_live_metadata_owner_gate_ready_count": 0,
            "runtime_gate_count": 0,
        },
        "release_lanes": [
            {
                "lane_id": "formal_gitea_merge",
                "status": "blocked_owner_response_required",
                "meaning": "由具備正式 Gitea 權限者合併 Wazuh 分支；不得 force push。",
                "runtime_authorized": False,
            },
            {
                "lane_id": "formal_patch_apply",
                "status": "blocked_owner_response_required",
                "meaning": "由正式 release lane 套用已驗證 patch set；不得跳過 production readback。",
                "runtime_authorized": False,
            },
            {
                "lane_id": "maintainer_local_push_with_safe_credential",
                "status": "blocked_safe_credential_required",
                "meaning": "只接受安全的 credential helper / SSH key / 正式 release token；不得使用明文 token workaround。",
                "runtime_authorized": False,
            },
        ],
        "blocked_actions": [
            "plain_text_gitea_token_in_remote_url",
            "copy_token_from_dirty_workspace",
            "force_push",
            "nginx_or_gateway_workaround_for_404",
            "docker_restart_for_wazuh_route",
            "k8s_or_argocd_manual_apply_for_wazuh_route",
            "firewall_change_for_wazuh_route",
            "wazuh_secret_or_manager_change_for_api_404",
            "enable_wazuh_live_metadata_without_owner_gate",
            "enable_wazuh_active_response",
            "host_write_or_kali_active_scan",
        ],
        "post_deploy_readback": {
            "required": True,
            "command": "python3 scripts/security/wazuh-readonly-production-readback.py --json",
            "must_not_return_http_404": True,
            "runtime_gate_expected": 0,
        },
        "execution_boundaries": {
            "not_authorization": True,
            "secret_value_collection_allowed": False,
            "plain_text_token_workaround_allowed": False,
            "force_push_allowed": False,
            "repo_write_authorized": False,
            "production_deploy_authorized": False,
            "runtime_execution_authorized": False,
            "wazuh_api_live_query_authorized": False,
            "wazuh_active_response_authorized": False,
            "host_write_authorized": False,
            "kali_active_scan_authorized": False,
        },
        "operator_interpretation": [
            "此 preflight 通過前，不得把 Gitea credential blocker 視為可繞過。",
            "正式 release 可以選 formal merge、formal patch apply 或安全 credential push，但都需要 owner response 與 deploy 後 readback。",
            "不得用 Nginx、Docker、K8s、firewall、Wazuh secret 或主機重啟來修 public API 404。",
            "Wazuh live metadata 查詢與 active response 是不同 gate；本 preflight 不授權任何 runtime action。",
        ],
    }


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


def validate(root: Path) -> None:
    snapshot_path = root / SNAPSHOT_PATH
    if not snapshot_path.exists():
        raise SystemExit(
            f"BLOCKED Wazuh release lane preflight snapshot missing: {SNAPSHOT_PATH.as_posix()}"
        )
    snapshot = load_json(snapshot_path)
    expected = build_report(snapshot.get("generated_at"))

    if snapshot.get("schema_version") != expected["schema_version"]:
        raise SystemExit("BLOCKED Wazuh release lane preflight schema_version mismatch")
    if snapshot.get("status") != expected["status"]:
        raise SystemExit("BLOCKED Wazuh release lane preflight status mismatch")
    for key, expected_value in expected["summary"].items():
        actual = snapshot.get("summary", {}).get(key)
        if actual != expected_value:
            raise SystemExit(
                f"BLOCKED Wazuh release lane preflight summary.{key}: "
                f"expected {expected_value!r}, got {actual!r}"
            )
    for key, value in snapshot.get("execution_boundaries", {}).items():
        if key == "not_authorization":
            if value is not True:
                raise SystemExit("BLOCKED Wazuh release lane preflight not_authorization must be true")
        elif value is not False:
            raise SystemExit(f"BLOCKED Wazuh release lane preflight execution_boundaries.{key}: expected false")


def main() -> int:
    parser = argparse.ArgumentParser(description="IwoooS Wazuh 只讀 API release lane preflight")
    parser.add_argument("--root", default=".", help="repository root")
    parser.add_argument("--output", help="寫出 JSON 報告")
    parser.add_argument("--generated-at", help="固定報告時間，供 committed snapshot 使用")
    args = parser.parse_args()

    root = Path(args.root).resolve()
    report = build_report(args.generated_at)
    if args.output:
        output = Path(args.output)
        output.parent.mkdir(parents=True, exist_ok=True)
        output.write_text(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    validate(root)
    summary = report["summary"]
    print(
        "WAZUH_READONLY_RELEASE_LANE_PREFLIGHT_OK "
        f"ready={summary['formal_release_lane_ready_count']} "
        f"acks={summary['accepted_ack_flag_count']}/{summary['required_ack_flag_count']} "
        f"evidence={summary['accepted_evidence_field_count']}/{summary['required_evidence_field_count']} "
        f"runtime_gate={summary['runtime_gate_count']}"
    )
    return 0


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