#!/usr/bin/env python3
"""Guard post-reboot recovery declarations against false green claims.

Read-only by design. It consumes post-reboot-readiness-summary.sh output, or
runs that summary when no file is provided. It never repairs services, writes
markers, sends owner requests, or changes 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]
SUMMARY_SCRIPT = ROOT / "scripts" / "reboot-recovery" / "post-reboot-readiness-summary.sh"

SCHEMA_VERSION = "awoooi_post_reboot_declaration_guard_v1"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Validate what may be declared after reboot recovery.",
    )
    parser.add_argument(
        "--summary-file",
        type=Path,
        help="Read an existing post-reboot readiness summary key/value file.",
    )
    parser.add_argument(
        "--proposed",
        action="append",
        default=[],
        help="Optional declaration to validate. Repeatable.",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        help="Print JSON instead of a compact status line.",
    )
    parser.add_argument(
        "--output",
        type=Path,
        help="Write JSON payload to this path.",
    )
    parser.add_argument(
        "--no-color",
        action="store_true",
        help="Pass --no-color when running the readiness summary.",
    )
    return parser.parse_args()


def run_summary(no_color: bool) -> str:
    cmd = [str(SUMMARY_SCRIPT)]
    if 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 not in (0, 2):
        raise SystemExit(
            "POST_REBOOT_DECLARATION_GUARD_FAILED "
            f"summary_rc={completed.returncode}\n{completed.stdout}"
        )
    return completed.stdout


def load_summary(args: argparse.Namespace) -> str:
    if args.summary_file:
        return args.summary_file.read_text(encoding="utf-8")
    return run_summary(no_color=args.no_color)


def parse_summary(text: str) -> dict[str, str]:
    values: dict[str, str] = {}
    for raw_line in text.splitlines():
        line = raw_line.strip()
        if not line or "=" not in line:
            continue
        key, value = line.split("=", 1)
        values[key.strip()] = value.strip()
    return values


def truthy(value: str | None) -> bool:
    return value in {"1", "true", "True", "yes", "YES"}


def nonzero(value: str | None) -> bool:
    if value is None or value in {"", "unknown"}:
        return False
    try:
        return int(value) != 0
    except ValueError:
        return False


def split_csv(value: str | None) -> list[str]:
    if not value or value == "none":
        return []
    return [item.strip() for item in value.split(",") if item.strip()]


def build_payload(summary: dict[str, str], proposed: list[str]) -> dict[str, Any]:
    allowed: list[str] = []
    forbidden: list[dict[str, str]] = []

    service_green = truthy(summary.get("SERVICE_GREEN"))
    product_data_green = truthy(summary.get("PRODUCT_DATA_GREEN"))
    backup_core_green = truthy(summary.get("BACKUP_CORE_GREEN"))
    dr_escrow_blocked = truthy(summary.get("DR_ESCROW_BLOCKED")) or nonzero(
        summary.get("ESCROW_MISSING_COUNT")
    )
    host_188_hygiene_blocked = truthy(summary.get("HOST_188_HYGIENE_BLOCKED"))
    wazuh_registry_accepted = truthy(summary.get("WAZUH_MANAGER_REGISTRY_ACCEPTED"))
    runtime_authorized = truthy(summary.get("RUNTIME_ACTION_AUTHORIZED"))
    next_required_gates = split_csv(summary.get("NEXT_REQUIRED_GATES"))
    overall_declaration = summary.get("OVERALL_DECLARATION", "unknown")

    if service_green:
        allowed.append("SERVICE_RECOVERY_GREEN")
    else:
        forbidden.append(
            {
                "declaration": "SERVICE_RECOVERY_GREEN",
                "reason": "service_green_not_1",
            }
        )

    if product_data_green:
        allowed.append("PRODUCT_DATA_GREEN")
    else:
        forbidden.append(
            {
                "declaration": "PRODUCT_DATA_GREEN",
                "reason": "product_data_green_not_1",
            }
        )

    if backup_core_green:
        allowed.append("BACKUP_CORE_GREEN")
    else:
        forbidden.append(
            {
                "declaration": "BACKUP_CORE_GREEN",
                "reason": "backup_core_green_not_1",
            }
        )

    if overall_declaration != "unknown":
        allowed.append(overall_declaration)

    if (
        service_green
        and product_data_green
        and backup_core_green
        and not dr_escrow_blocked
        and not host_188_hygiene_blocked
        and wazuh_registry_accepted
        and not runtime_authorized
    ):
        allowed.append("FULL_STACK_GREEN")
    else:
        full_stack_reasons: list[str] = []
        if not service_green:
            full_stack_reasons.append("service_green_not_1")
        if not product_data_green:
            full_stack_reasons.append("product_data_green_not_1")
        if not backup_core_green:
            full_stack_reasons.append("backup_core_green_not_1")
        if dr_escrow_blocked:
            full_stack_reasons.append(
                f"escrow_missing_count:{summary.get('ESCROW_MISSING_COUNT', 'unknown')}"
            )
        if host_188_hygiene_blocked:
            full_stack_reasons.append("host_188_hygiene_blocked:1")
        if not wazuh_registry_accepted:
            full_stack_reasons.append("wazuh_manager_registry_accepted:0")
        if runtime_authorized:
            full_stack_reasons.append("runtime_action_authorized:1")
        forbidden.append(
            {
                "declaration": "FULL_STACK_GREEN",
                "reason": ",".join(full_stack_reasons) or "unknown",
            }
        )

    if dr_escrow_blocked:
        forbidden.append(
            {
                "declaration": "DR_COMPLETE",
                "reason": f"escrow_missing_count:{summary.get('ESCROW_MISSING_COUNT', 'unknown')}",
            }
        )
    else:
        allowed.append("DR_COMPLETE")

    if host_188_hygiene_blocked:
        forbidden.append(
            {
                "declaration": "HOST_188_FULLY_GREEN",
                "reason": "host_188_hygiene_blocked:1",
            }
        )
    else:
        allowed.append("HOST_188_FULLY_GREEN")

    if not wazuh_registry_accepted:
        forbidden.append(
            {
                "declaration": "WAZUH_REGISTRY_RECOVERED",
                "reason": "wazuh_manager_registry_accepted:0",
            }
        )
    else:
        allowed.append("WAZUH_REGISTRY_RECOVERED")

    if not runtime_authorized:
        forbidden.append(
            {
                "declaration": "RUNTIME_ACTION_AUTHORIZED",
                "reason": "runtime_action_authorized:0",
            }
        )
    else:
        allowed.append("RUNTIME_ACTION_AUTHORIZED")

    allowed_set = set(allowed)
    forbidden_map = {item["declaration"]: item["reason"] for item in forbidden}
    rejected_proposed = [
        {"declaration": item, "reason": forbidden_map[item]}
        for item in proposed
        if item in forbidden_map
    ]
    accepted_proposed = [item for item in proposed if item in allowed_set]
    unknown_proposed = [
        item
        for item in proposed
        if item not in allowed_set and item not in forbidden_map
    ]

    if not service_green:
        status = "blocked_service_recovery"
    elif not product_data_green:
        status = "blocked_product_data_recovery"
    else:
        status = "allowed_with_boundary_blockers"
    if rejected_proposed:
        status = "blocked_false_green_proposal"

    return {
        "schema_version": SCHEMA_VERSION,
        "generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
        "source": {
            "summary_script": str(SUMMARY_SCRIPT.relative_to(ROOT)),
            "artifact_dir": summary.get("ARTIFACT_DIR", "unknown"),
            "overall_declaration": overall_declaration,
        },
        "status": status,
        "allowed_declarations": sorted(set(allowed)),
        "forbidden_declarations": forbidden,
        "next_required_gates": next_required_gates,
        "counts": {
            "allowed_count": len(set(allowed)),
            "forbidden_count": len(forbidden),
            "next_required_gate_count": len(next_required_gates),
            "proposed_count": len(proposed),
            "accepted_proposed_count": len(accepted_proposed),
            "rejected_proposed_count": len(rejected_proposed),
            "unknown_proposed_count": len(unknown_proposed),
        },
        "proposed": {
            "accepted": accepted_proposed,
            "rejected": rejected_proposed,
            "unknown": unknown_proposed,
        },
        "evidence": {
            "service_green": summary.get("SERVICE_GREEN", "unknown"),
            "product_data_green": summary.get("PRODUCT_DATA_GREEN", "unknown"),
            "stock_freshness_status": summary.get("STOCK_FRESHNESS_STATUS", "unknown"),
            "stock_latest_trading_date": summary.get(
                "STOCK_LATEST_TRADING_DATE",
                "unknown",
            ),
            "stock_blockers": summary.get("STOCK_BLOCKERS", "unknown"),
            "stock_eod_window_pending": summary.get(
                "STOCK_EOD_WINDOW_PENDING",
                "unknown",
            ),
            "stock_eod_classification": summary.get(
                "STOCK_EOD_CLASSIFICATION",
                "unknown",
            ),
            "stock_eod_next_action": summary.get("STOCK_EOD_NEXT_ACTION", "unknown"),
            "backup_core_green": summary.get("BACKUP_CORE_GREEN", "unknown"),
            "escrow_missing_count": summary.get("ESCROW_MISSING_COUNT", "unknown"),
            "host_188_hygiene_blocked": summary.get("HOST_188_HYGIENE_BLOCKED", "unknown"),
            "wazuh_manager_registry_accepted": summary.get(
                "WAZUH_MANAGER_REGISTRY_ACCEPTED",
                "unknown",
            ),
            "wazuh_route_code": summary.get("WAZUH_ROUTE_CODE", "unknown"),
            "wazuh_transport_count": summary.get("WAZUH_TRANSPORT_COUNT", "unknown"),
            "wazuh_coverage_scope": summary.get("WAZUH_COVERAGE_SCOPE", "unknown"),
            "wazuh_direct_active": summary.get("WAZUH_DIRECT_ACTIVE", "unknown"),
            "wazuh_no_transport": summary.get("WAZUH_NO_TRANSPORT", "unknown"),
            "wazuh_ssh_blocked": summary.get("WAZUH_SSH_BLOCKED", "unknown"),
            "wazuh_dashboard_api_connection": summary.get(
                "WAZUH_DASHBOARD_API_CONNECTION",
                "unknown",
            ),
            "wazuh_dashboard_index_ok": summary.get("WAZUH_DASHBOARD_INDEX_OK", "unknown"),
            "runtime_action_authorized": summary.get("RUNTIME_ACTION_AUTHORIZED", "unknown"),
        },
        "runtime_write_performed": False,
    }


def main() -> int:
    args = parse_args()
    summary = parse_summary(load_summary(args))
    payload = build_payload(summary, args.proposed)
    serialized = json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)

    if args.output:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_text(serialized + "\n", encoding="utf-8")

    if args.json:
        print(serialized)
    else:
        prefix = "POST_REBOOT_DECLARATION_GUARD_OK"
        if payload["proposed"]["rejected"]:
            prefix = "POST_REBOOT_DECLARATION_GUARD_REJECTED"
        elif payload["status"] == "blocked_service_recovery":
            prefix = "POST_REBOOT_DECLARATION_GUARD_BLOCKED"
        print(
            f"{prefix} "
            f"status={payload['status']} "
            f"allowed={payload['counts']['allowed_count']} "
            f"forbidden={payload['counts']['forbidden_count']} "
            f"next_gates={payload['counts']['next_required_gate_count']} "
            f"rejected_proposed={payload['counts']['rejected_proposed_count']}"
        )

    if payload["proposed"]["rejected"]:
        return 2
    if payload["status"] == "blocked_service_recovery":
        return 2
    return 0


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