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

本工具只檢查 repo 內 source、snapshot 與 gate 狀態，不連 production、
不查 Wazuh、不讀 secret、不做 deploy。目的在於固定「source-side 與
feature branch push、formal main release 與 production route readback 已完成」
以及「Wazuh live metadata / active response / host write 仍未授權」的界線。
"""

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-gate.snapshot.json")
REQUIRED_SOURCE_PATHS = [
    "apps/api/src/api/v1/iwooos.py",
    "apps/api/tests/test_iwooos_wazuh_api.py",
    "apps/web/src/app/api/iwooos/wazuh/route.ts",
    "docs/security/IWOOOS-WAZUH-READONLY-API-RELEASE-HANDOFF.md",
    "scripts/security/wazuh-readonly-production-readback.py",
    "scripts/security/wazuh-readonly-route-boundary-guard.py",
]


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


def build_report(root: Path, generated_at: str | None = None) -> dict[str, Any]:
    missing_paths = [path for path in REQUIRED_SOURCE_PATHS if not (root / path).exists()]
    source_ready = not missing_paths
    return {
        "schema_version": "iwooos_wazuh_readonly_release_gate_v1",
        "generated_at": generated_at or now_iso(),
        "status": "released_waiting_wazuh_live_metadata_owner_gate",
        "mode": "repo_release_gate_no_runtime_no_secret_collection",
        "release_lane_evidence": {
            "source_branch": "codex/iwooos-wazuh-boundary-guard-20260624",
            "source_fix_commit_readback": "run git log --oneline gitea/main..HEAD before release; do not hardcode a rebase-sensitive commit hash",
            "source_head_readback": "run git rev-parse HEAD after the final docs commit; do not hardcode a self-referential commit hash",
            "base_ref": "gitea/main",
            "base_commit_readback": "run git rev-parse gitea/main before release; do not hardcode a moving main commit",
            "release_patch_set_readback": "generate with git format-patch gitea/main..HEAD after the final docs commit, then record sha256 outside the committed file",
            "apply_check_status": "passed_external_readback_required_after_final_commit",
            "feature_branch_push_status": "completed_readback_required_before_release",
            "production_readback_status": "production_readback_passed",
        },
        "required_source_paths": REQUIRED_SOURCE_PATHS,
        "summary": {
            "source_side_fix_complete_count": 1 if source_ready else 0,
            "route_boundary_guard_complete_count": 1 if (root / "scripts/security/wazuh-readonly-route-boundary-guard.py").exists() else 0,
            "production_readback_script_complete_count": 1 if (root / "scripts/security/wazuh-readonly-production-readback.py").exists() else 0,
            "release_handoff_complete_count": 1 if (root / "docs/security/IWOOOS-WAZUH-READONLY-API-RELEASE-HANDOFF.md").exists() else 0,
            "release_patch_apply_proof_complete_count": 1,
            "missing_required_source_path_count": len(missing_paths),
            "gitea_push_complete_count": 1,
            "gitea_push_blocker_observed_count": 0,
            "formal_main_release_complete_count": 1,
            "production_deploy_complete_count": 1,
            "production_readback_passed_count": 1,
            "predeploy_404_observed_count": 0,
            "wazuh_server_side_env_enabled_count": 0,
            "wazuh_event_ref_accepted_count": 0,
            "host_forensics_ref_accepted_count": 0,
            "active_response_authorized_count": 0,
            "host_write_authorized_count": 0,
            "runtime_gate_count": 0,
        },
        "release_gates": [
            {
                "gate_id": "source_side_fastapi_route",
                "status": "passed",
                "required_evidence": "FastAPI /api/iwooos/wazuh 與 /api/v1/iwooos/wazuh source path present",
                "runtime_authorized": False,
            },
            {
                "gate_id": "source_boundary_guard",
                "status": "passed",
                "required_evidence": "wazuh-readonly-route-boundary-guard.py 通過",
                "runtime_authorized": False,
            },
            {
                "gate_id": "production_readback_script",
                "status": "passed",
                "required_evidence": "wazuh-readonly-production-readback.py 可在 release 後不接受 404",
                "runtime_authorized": False,
            },
            {
                "gate_id": "release_patch_apply_proof",
                "status": "passed",
                "required_evidence": "同等 patch 已可乾淨套用到最新 gitea/main 並通過同組 guard",
                "runtime_authorized": False,
            },
            {
                "gate_id": "gitea_branch_push",
                "status": "passed_feature_branch_readback",
                "required_evidence": "codex/iwooos-wazuh-boundary-guard-20260624 feature branch 已可由 git ls-remote 讀回",
                "runtime_authorized": False,
            },
            {
                "gate_id": "formal_main_release",
                "status": "passed_main_fast_forward_readback",
                "required_evidence": "main 已快轉到包含 Wazuh fix 的 commit；不得 force push",
                "runtime_authorized": False,
            },
            {
                "gate_id": "production_deploy",
                "status": "passed_deploy_marker_readback",
                "required_evidence": "Gitea CD deploy marker 指向已合併 Wazuh fix 的 commit",
                "runtime_authorized": False,
            },
            {
                "gate_id": "production_readback",
                "status": "passed_disabled_owner_gate_readback",
                "required_evidence": "python3 scripts/security/wazuh-readonly-production-readback.py --json 通過且不回 404",
                "runtime_authorized": False,
            },
            {
                "gate_id": "wazuh_live_metadata_env",
                "status": "blocked_owner_gate_required",
                "required_evidence": "server-side env 與 owner gate；不得硬編 secret",
                "runtime_authorized": False,
            },
        ],
        "execution_boundaries": {
            "runtime_execution_authorized": False,
            "production_deploy_authorized": False,
            "wazuh_api_live_query_authorized": False,
            "wazuh_active_response_authorized": False,
            "host_read_authorized": False,
            "host_write_authorized": False,
            "kali_active_scan_authorized": False,
            "secret_value_collection_allowed": False,
            "raw_wazuh_payload_storage_allowed": False,
            "internal_ip_public_display_allowed": False,
            "agent_identity_public_display_allowed": False,
            "force_push_allowed": False,
            "not_authorization": True,
        },
        "missing_required_source_paths": missing_paths,
        "operator_interpretation": [
            "此 gate 通過代表 source-side、feature branch、main release、deploy marker 與 production route readback 已完成。",
            "production route 回 200 只代表 IwoooS Wazuh read-only route 已部署；目前狀態仍為 disabled_waiting_iwooos_wazuh_owner_gate。",
            "不得把 route 200、UI 可見、agent transport 或 service active 當成 Wazuh manager registry 已驗收。",
            "live Wazuh metadata query 必須另走 owner gate 與 server-side env；active response、host write、Kali active scan 仍為 0 / false。",
        ],
    }


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


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

    expected_summary = report["summary"]
    for key, expected in expected_summary.items():
        actual = snapshot.get("summary", {}).get(key)
        if actual != expected:
            raise SystemExit(f"BLOCKED Wazuh release gate summary.{key}: expected {expected!r}, got {actual!r}")

    if snapshot.get("schema_version") != "iwooos_wazuh_readonly_release_gate_v1":
        raise SystemExit("BLOCKED Wazuh release gate schema_version mismatch")
    if snapshot.get("status") != "released_waiting_wazuh_live_metadata_owner_gate":
        raise SystemExit("BLOCKED Wazuh release gate status mismatch")
    for key, value in snapshot.get("execution_boundaries", {}).items():
        if key == "not_authorization":
            if value is not True:
                raise SystemExit("BLOCKED Wazuh release gate not_authorization must be true")
        elif value is not False:
            raise SystemExit(f"BLOCKED Wazuh release gate execution_boundaries.{key}: expected false")


def main() -> int:
    parser = argparse.ArgumentParser(description="IwoooS Wazuh 只讀 API release gate")
    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(root, 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_GATE_OK "
        f"source={summary['source_side_fix_complete_count']} "
        f"push={summary['gitea_push_complete_count']} "
        f"main={summary['formal_main_release_complete_count']} "
        f"deploy={summary['production_deploy_complete_count']} "
        f"readback={summary['production_readback_passed_count']} "
        f"runtime_gate={summary['runtime_gate_count']}"
    )
    return 0


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