#!/usr/bin/env python3
"""Generate a fail-closed owner response template for active post-reboot gates.

Read-only by design. This script only turns an owner-packet artifact into a
placeholder response JSON. It never sends requests, reads secrets, writes
credential markers, or modifies host/runtime state.
"""

from __future__ import annotations

import argparse
import json
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[2]
OWNER_PACKET_GENERATOR = (
    ROOT / "scripts" / "reboot-recovery" / "post-reboot-next-gate-owner-packets.py"
)

EXPECTED_OWNER_PACKET_SCHEMA = "awoooi_post_reboot_next_gate_owner_packets_v1"
RESPONSE_SCHEMA = "awoooi_post_reboot_next_gate_owner_response_v1"

ESCROW_ITEM_IDS = [
    "restic_repository_password",
    "offsite_provider_credentials",
    "break_glass_admin_credentials",
    "dns_registrar_recovery",
    "oauth_ai_provider_recovery",
]

EXPECTED_HOST_ALIASES = [
    "core-110",
    "gateway-188",
    "k3s-control-120",
    "k3s-control-121",
    "security-observer-112",
    "dev-workstation-111",
]


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Create a placeholder owner response JSON for active post-reboot gates.",
    )
    parser.add_argument(
        "--owner-packet-file",
        type=Path,
        help="Use an existing post-reboot owner packet JSON.",
    )
    parser.add_argument(
        "--dispatch-file",
        type=Path,
        help="Generate owner packets from an existing next-gate dispatch file.",
    )
    parser.add_argument(
        "--summary-file",
        type=Path,
        help="Generate owner packets from an existing readiness summary file.",
    )
    parser.add_argument(
        "--output",
        type=Path,
        help="Write JSON to this path instead of stdout.",
    )
    parser.add_argument(
        "--no-color",
        action="store_true",
        help="Pass --no-color when generating owner packets.",
    )
    return parser.parse_args()


def load_json(path: Path, label: str) -> dict[str, Any]:
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except FileNotFoundError as exc:
        raise SystemExit(f"{label}_not_found={path}") from exc
    except json.JSONDecodeError as exc:
        raise SystemExit(f"{label}_json_invalid={exc}") from exc
    if not isinstance(payload, dict):
        raise SystemExit(f"{label}_json_not_object")
    return payload


def generate_owner_packet(args: argparse.Namespace) -> dict[str, Any]:
    cmd = [str(OWNER_PACKET_GENERATOR)]
    if args.dispatch_file:
        cmd.extend(["--dispatch-file", str(args.dispatch_file)])
    if args.summary_file:
        cmd.extend(["--summary-file", str(args.summary_file)])
    if args.no_color:
        cmd.append("--no-color")

    completed = subprocess.run(
        cmd,
        cwd=ROOT,
        check=False,
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
    )
    if completed.returncode != 0:
        raise SystemExit(
            "owner_packet_generation_failed "
            f"rc={completed.returncode}\n{completed.stdout}"
        )
    try:
        packet = json.loads(completed.stdout)
    except json.JSONDecodeError as exc:
        raise SystemExit(f"owner_packet_json_invalid={exc}") from exc
    if not isinstance(packet, dict):
        raise SystemExit("owner_packet_json_not_object")
    return packet


def load_owner_packet(args: argparse.Namespace) -> dict[str, Any]:
    if args.owner_packet_file:
        return load_json(args.owner_packet_file, label="owner_packet_file")
    return generate_owner_packet(args)


def as_list(value: Any) -> list[Any]:
    if value is None:
        return []
    if isinstance(value, list):
        return value
    return [value]


def ensure_owner_packet(packet: dict[str, Any]) -> list[dict[str, Any]]:
    if packet.get("schema_version") != EXPECTED_OWNER_PACKET_SCHEMA:
        raise SystemExit(f"owner_packet_schema={packet.get('schema_version')!r}")
    owner_packets = [item for item in as_list(packet.get("owner_packets")) if isinstance(item, dict)]
    source = packet.get("source", {})
    if isinstance(source, dict):
        expected = {str(item) for item in as_list(source.get("next_required_gates"))}
        actual = {str(item.get("packet_id")) for item in owner_packets if item.get("packet_id")}
        if expected and actual != expected:
            raise SystemExit(f"owner_packet_gate_mismatch expected={sorted(expected)} actual={sorted(actual)}")
    return owner_packets


def common_response(packet: dict[str, Any]) -> dict[str, Any]:
    return {
        "gate_id": str(packet.get("packet_id", "unknown")),
        "owner_role": "owner_role_here",
        "owner_team": "owner_team_here",
        "decision": "pending",
        "decision_reason": "decision_reason_here",
        "affected_scope": str(packet.get("title", "post-reboot owner evidence")),
        "redacted_evidence_refs": [
            "redacted_evidence_ref_here",
        ],
        "followup_owner": "followup_owner_here",
        "runtime_action_requested": False,
        "runtime_action_authorized": False,
        "host_write_requested": False,
        "host_write_authorized": False,
        "secret_value_included": False,
        "secret_value_collection_allowed": False,
    }


def credential_escrow_response(packet: dict[str, Any]) -> dict[str, Any]:
    item_ids = [
        str(item)
        for item in as_list(packet.get("required_items"))
        if str(item) in ESCROW_ITEM_IDS
    ] or ESCROW_ITEM_IDS
    response = common_response(packet)
    response["credential_marker_write_requested"] = False
    response["credential_marker_write_authorized"] = False
    response["escrow_items"] = [
        {
            "item_id": item_id,
            "non_secret_evidence_ref": "non_secret_evidence_ref_here",
            "recovery_owner": "owner_role_here",
            "reviewer": "reviewer_here",
            "last_reviewed_at": "pending",
            "contains_secret_value": False,
        }
        for item_id in item_ids
    ]
    return response


def wazuh_registry_response(packet: dict[str, Any]) -> dict[str, Any]:
    response = common_response(packet)
    response.update(
        {
            "wazuh_active_response_requested": False,
            "wazuh_active_response_authorized": False,
            "agent_reenroll_requested": False,
            "wazuh_restart_requested": False,
            "kali_active_scan_requested": False,
            "registry_export_ref": "registry_export_ref_here",
            "registry_time_window": "pending",
            "expected_host_aliases": EXPECTED_HOST_ALIASES,
            "manager_registry_count": 0,
            "dashboard_api_connection_status": "pending",
            "dashboard_api_version_status": "pending",
            "reviewer": "reviewer_here",
        }
    )
    return response


def generic_unsupported_response(packet: dict[str, Any]) -> dict[str, Any]:
    response = common_response(packet)
    response["gate_specific_evidence_required"] = [
        "update_post_reboot_owner_response_preflight_for_this_gate",
    ]
    return response


def response_for(packet: dict[str, Any]) -> dict[str, Any]:
    gate_id = str(packet.get("packet_id", ""))
    if gate_id == "credential_escrow_evidence":
        return credential_escrow_response(packet)
    if gate_id == "wazuh_manager_registry_export":
        return wazuh_registry_response(packet)
    return generic_unsupported_response(packet)


def build_template(packet: dict[str, Any]) -> dict[str, Any]:
    owner_packets = ensure_owner_packet(packet)
    gate_ids = [
        str(item.get("packet_id"))
        for item in owner_packets
        if item.get("packet_id")
    ]
    return {
        "schema_version": RESPONSE_SCHEMA,
        "generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
        "template_status": "placeholder_fail_closed_until_redacted_owner_evidence_is_supplied",
        "source_owner_packet_schema": packet.get("schema_version", "unknown"),
        "active_gate_count": len(gate_ids),
        "active_gates": gate_ids,
        "responses": [response_for(item) for item in owner_packets],
    }


def main() -> int:
    args = parse_args()
    packet = load_owner_packet(args)
    template = build_template(packet)
    payload = json.dumps(template, ensure_ascii=False, indent=2, sort_keys=True)
    if args.output:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_text(payload + "\n", encoding="utf-8")
    else:
        print(payload)
    return 0


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