#!/usr/bin/env python3
"""
IwoooS Wazuh 只讀 API release owner request 草稿。

本工具只產生 / 驗證 repo 內 committed snapshot；不送 request、不讀
credential、不推送、不部署、不查 Wazuh、不改 runtime。
"""

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-owner-request.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",
]

FORBIDDEN_PAYLOADS = [
    "token",
    "secret",
    "private_key",
    "cookie",
    "session",
    "authorization_header",
    "runner_token",
    "webhook_secret",
    "wazuh_password",
    "wazuh_raw_payload",
    "git_credential",
    "repo_archive",
]

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",
]

HANDOFF_ENVELOPE_FIELDS = [
    "request_id",
    "stage_id",
    "recipient_role_or_team",
    "sender_role_or_team",
    "requested_response_window",
    "allowed_release_methods",
    "required_ack_flags",
    "required_evidence_fields",
    "target_branch_or_patch_set",
    "post_deploy_readback_command",
    "forbidden_payloads",
    "blocked_runtime_actions",
    "followup_owner",
    "not_approval",
]


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_owner_request_v1",
        "generated_at": generated_at or now_iso(),
        "status": "draft_not_dispatched_waiting_release_lane_owner",
        "mode": "repo_request_draft_no_secret_no_runtime_no_push",
        "summary": {
            "request_draft_count": 1,
            "required_ack_flag_count": len(REQUIRED_ACK_FLAGS),
            "required_evidence_field_count": len(REQUIRED_EVIDENCE_FIELDS),
            "allowed_release_method_count": len(ALLOWED_RELEASE_METHODS),
            "forbidden_payload_count": len(FORBIDDEN_PAYLOADS),
            "blocked_action_count": len(BLOCKED_ACTIONS),
            "handoff_envelope_field_count": len(HANDOFF_ENVELOPE_FIELDS),
            "request_sent_count": 0,
            "recipient_confirmed_count": 0,
            "owner_response_received_count": 0,
            "owner_response_accepted_count": 0,
            "formal_release_lane_ready_count": 0,
            "gitea_push_authorized_count": 0,
            "patch_apply_authorized_count": 0,
            "production_deploy_authorized_count": 0,
            "production_readback_passed_count": 0,
            "runtime_gate_count": 0,
        },
        "request_draft": {
            "request_id": "iwooos_wazuh_readonly_release_owner_request",
            "stage_id": "P0-IWOOOS-WAZUH-RELEASE",
            "recipient_role_or_team": "pending_release_lane_owner",
            "sender_role_or_team": "iwooos_security_reviewer",
            "requested_response_window": "not_scheduled",
            "allowed_release_methods": ALLOWED_RELEASE_METHODS,
            "required_ack_flags": REQUIRED_ACK_FLAGS,
            "required_evidence_fields": REQUIRED_EVIDENCE_FIELDS,
            "target_branch": "codex/iwooos-wazuh-boundary-guard-20260624",
            "target_branch_readback": "git log --oneline gitea/main..HEAD",
            "target_patch_set_readback": "git format-patch gitea/main..HEAD after final docs commit; record sha256 outside committed docs",
            "post_deploy_readback_command": "python3 scripts/security/wazuh-readonly-production-readback.py --json",
            "redacted_evidence_refs": [
                "docs/security/IWOOOS-WAZUH-READONLY-API-RELEASE-HANDOFF.md",
                "docs/security/wazuh-readonly-release-gate.snapshot.json",
                "docs/security/wazuh-readonly-release-lane-preflight.snapshot.json",
            ],
            "forbidden_payloads": FORBIDDEN_PAYLOADS,
            "blocked_runtime_actions": BLOCKED_ACTIONS,
            "followup_owner": "pending_followup_owner",
            "not_approval": True,
            "request_sent": False,
            "recipient_confirmed": False,
            "owner_response_received": False,
            "owner_response_accepted": False,
            "runtime_gate": False,
            "action_buttons_allowed": False,
        },
        "execution_boundaries": {
            "dispatch_authorized": False,
            "request_sent": False,
            "recipient_confirmed": False,
            "repo_write_authorized": False,
            "gitea_push_authorized": False,
            "patch_apply_authorized": False,
            "production_deploy_authorized": False,
            "runtime_execution_authorized": False,
            "secret_value_collection_allowed": False,
            "plain_text_token_workaround_allowed": False,
            "force_push_allowed": False,
            "wazuh_api_live_query_authorized": False,
            "wazuh_active_response_authorized": False,
            "host_write_authorized": False,
            "kali_active_scan_authorized": False,
            "not_authorization": True,
        },
        "handoff_envelope_fields": HANDOFF_ENVELOPE_FIELDS,
        "send_after_conditions": [
            "先確認 gitea/main、Wazuh 分支與另一個 AwoooP Session 基線。",
            "只送脫敏欄位與 refs；不得附 secret、raw Wazuh payload、git credential 或 runtime 操作要求。",
            "一般批准繼續不是 release owner response。",
            "收到 response 後仍需先通過 owner response acceptance ledger，不能直接 push 或 deploy。",
        ],
    }


def validate(root: Path) -> None:
    snapshot_path = root / SNAPSHOT_PATH
    if not snapshot_path.exists():
        raise SystemExit(f"BLOCKED Wazuh release owner request snapshot missing: {SNAPSHOT_PATH}")
    snapshot = json.loads(snapshot_path.read_text(encoding="utf-8"))
    expected = build_report(snapshot.get("generated_at"))

    for key in ("schema_version", "status", "mode"):
        if snapshot.get(key) != expected[key]:
            raise SystemExit(f"BLOCKED Wazuh release owner request {key} 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 owner request 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 owner request not_authorization must be true")
        elif value is not False:
            raise SystemExit(f"BLOCKED Wazuh release owner request execution_boundaries.{key}: expected false")


def main() -> int:
    parser = argparse.ArgumentParser(description="IwoooS Wazuh 只讀 API release owner request 草稿")
    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_OWNER_REQUEST_OK "
        f"drafts={summary['request_draft_count']} "
        f"sent={summary['request_sent_count']} "
        f"accepted={summary['owner_response_accepted_count']} "
        f"runtime_gate={summary['runtime_gate_count']}"
    )
    return 0


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