"""
P2-415 AI Agent controlled executor handoff readback.

This loader validates the committed controlled executor handoff runway. It makes
high-risk controlled apply packets visible to the product, while keeping the
route itself read-only: no live apply, Telegram send, Bot API, secret read,
host write, kubectl action, or destructive operation is executed here.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from src.services.snapshot_paths import default_evaluations_dir

_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__))
_SNAPSHOT_PATTERN = "ai_agent_controlled_executor_handoff_*.json"
_SCHEMA_VERSION = "ai_agent_controlled_executor_handoff_v1"
_RUNTIME_AUTHORITY = "controlled_executor_handoff_readback_no_live_apply"
_EXPECTED_CURRENT_TASK = "P2-415"
_EXPECTED_NEXT_TASK = "P2-416"
_EXPECTED_SOURCE_SCHEMAS = {
    "ai_agent_high_risk_owner_review_queue_v1",
    "ai_agent_action_audit_ledger_v1",
    "ai_agent_action_owner_acceptance_event_bus_v1",
    "ai_agent_report_runtime_readiness_v1",
    "ai_agent_runtime_write_gate_review_v1",
    "ai_agent_post_write_verifier_package_v1",
    "ai_agent_learning_writeback_approval_package_v1",
    "ai_agent_telegram_receipt_approval_package_v1",
}
_TRUE_TRUTH_FLAGS = {
    "p2_409_controlled_apply_queue_loaded",
    "p2_410_audit_ledger_loaded",
    "p2_411_handoff_event_bus_loaded",
    "runtime_readiness_loaded",
    "runtime_write_gate_loaded",
    "post_write_verifier_loaded",
    "learning_writeback_loaded",
    "telegram_receipt_loaded",
    "high_risk_controlled_executor_handoff_ready",
    "critical_break_glass_required",
    "allowlist_route_required",
    "ansible_check_mode_required",
    "rollback_plan_required",
    "post_action_verifier_required",
    "telegram_evidence_required",
    "km_writeback_required",
    "playbook_trust_writeback_required",
}
_FALSE_TRUTH_FLAGS = {
    "high_risk_owner_review_required",
    "controlled_executor_dispatch_enabled",
    "live_apply_enabled",
    "critical_auto_bypass_allowed",
    "gateway_queue_write_enabled",
    "telegram_send_enabled",
    "bot_api_call_enabled",
    "km_write_enabled",
    "playbook_trust_write_enabled",
    "production_write_enabled",
    "secret_read_enabled",
    "paid_api_call_enabled",
    "host_write_enabled",
    "kubectl_action_enabled",
    "destructive_operation_enabled",
}
_ZERO_TRUTH_COUNTS = {
    "controlled_executor_dispatch_count_24h",
    "live_apply_count_24h",
    "gateway_queue_write_count_24h",
    "telegram_send_count_24h",
    "bot_api_call_count_24h",
    "km_write_count_24h",
    "playbook_trust_write_count_24h",
    "production_write_count_24h",
    "secret_read_count_24h",
    "paid_api_call_count_24h",
    "host_write_count_24h",
    "kubectl_action_count_24h",
    "destructive_operation_count_24h",
}
_TRUE_BOUNDARY_FLAGS = {
    "committed_snapshot_read_allowed",
    "controlled_executor_handoff_preview_allowed",
    "ansible_check_mode_receipt_preview_allowed",
    "mcp_tool_registry_route_preview_allowed",
    "post_action_verifier_binding_preview_allowed",
    "telegram_evidence_preview_allowed",
    "km_playbook_trust_writeback_preview_allowed",
}
_FALSE_BOUNDARY_FLAGS = {
    "controlled_executor_dispatch_enabled",
    "live_apply_enabled",
    "gateway_queue_write_enabled",
    "telegram_send_enabled",
    "bot_api_call_enabled",
    "km_write_enabled",
    "playbook_trust_write_enabled",
    "production_write_enabled",
    "secret_read_enabled",
    "paid_api_call_enabled",
    "host_write_enabled",
    "kubectl_action_enabled",
    "destructive_operation_enabled",
}
_ZERO_ROLLUP_FIELDS = {
    "controlled_executor_dispatch_count",
    "live_apply_count",
    "gateway_queue_write_count",
    "telegram_send_count",
    "bot_api_call_count",
    "km_write_count",
    "playbook_trust_write_count",
    "production_write_count",
    "secret_read_count",
    "paid_api_call_count",
    "host_write_count",
    "kubectl_action_count",
    "destructive_operation_count",
}
_FORBIDDEN_PUBLIC_TERMS = {
    "批准" + "！",
    "In app " + "browser",
    "My request for " + "Codex",
    "codex_" + "delegation",
    "source_" + "thread_id",
    "chain_of_thought",
    "private reasoning text",
    "authorization_header",
    "telegram token value",
    "raw_payload",
    "raw prompt",
    "internal collaboration transcript",
    "工作視窗",
    "對話內容",
}


def load_latest_ai_agent_controlled_executor_handoff(
    evaluations_dir: Path | None = None,
) -> dict[str, Any]:
    """Load the newest committed P2-415 controlled executor handoff snapshot."""
    directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
    candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
    if not candidates:
        raise FileNotFoundError(f"no AI Agent controlled executor handoff snapshots found in {directory}")

    latest = candidates[-1]
    with latest.open(encoding="utf-8") as handle:
        payload = json.load(handle)

    if not isinstance(payload, dict):
        raise ValueError(f"{latest}: expected JSON object")

    label = str(latest)
    _require_schema(payload, label)
    _require_sources(payload, label)
    _require_truth(payload, label)
    _require_packets(payload, label)
    _require_routes(payload, label)
    _require_verifier_bindings(payload, label)
    _require_learning_contracts(payload, label)
    _require_boundaries(payload, label)
    _require_redaction_contract(payload, label)
    _require_rollups(payload, label)
    _require_no_forbidden_public_terms(payload, label)
    return payload


def _require_schema(payload: dict[str, Any], label: str) -> None:
    if payload.get("schema_version") != _SCHEMA_VERSION:
        raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}")
    status = payload.get("program_status") or {}
    expected = {
        "overall_completion_percent": 100,
        "current_priority": "P0",
        "current_task_id": _EXPECTED_CURRENT_TASK,
        "next_task_id": _EXPECTED_NEXT_TASK,
        "read_only_mode": True,
        "runtime_authority": _RUNTIME_AUTHORITY,
    }
    mismatches = _mismatches(status, expected)
    if mismatches:
        raise ValueError(f"{label}: program_status mismatch: {mismatches}")
    if not status.get("status_note"):
        raise ValueError(f"{label}: program_status.status_note is required")


def _require_sources(payload: dict[str, Any], label: str) -> None:
    sources = payload.get("source_readbacks") or []
    schemas = {item.get("source_schema_version") for item in sources}
    if schemas != _EXPECTED_SOURCE_SCHEMAS:
        raise ValueError(f"{label}: source schemas mismatch: {sorted(schemas)}")
    for source in sources:
        if source.get("status") != "loaded":
            raise ValueError(f"{label}: source {source.get('readback_id')} must be loaded")


def _require_truth(payload: dict[str, Any], label: str) -> None:
    truth = payload.get("handoff_truth") or {}
    missing = sorted(field for field in _TRUE_TRUTH_FLAGS if truth.get(field) is not True)
    if missing:
        raise ValueError(f"{label}: handoff truth flags must remain true: {missing}")
    unsafe = sorted(field for field in _FALSE_TRUTH_FLAGS if truth.get(field) is not False)
    if unsafe:
        raise ValueError(f"{label}: live/write/unsafe truth flags must remain false: {unsafe}")
    non_zero = sorted(field for field in _ZERO_TRUTH_COUNTS if truth.get(field) != 0)
    if non_zero:
        raise ValueError(f"{label}: live/write/unsafe truth counts must remain zero: {non_zero}")
    if not truth.get("truth_note"):
        raise ValueError(f"{label}: handoff_truth.truth_note is required")


def _require_packets(payload: dict[str, Any], label: str) -> None:
    packets = payload.get("executor_handoff_packets") or []
    if len(packets) != 7:
        raise ValueError(f"{label}: executor_handoff_packets must contain 7 items")

    high_ready = 0
    critical_break_glass = 0
    seen: set[str] = set()
    for packet in packets:
        packet_id = packet.get("packet_id")
        if not packet_id or packet_id in seen:
            raise ValueError(f"{label}: packet_id must be unique")
        seen.add(packet_id)

        if packet.get("live_apply_performed") is not False or packet.get("side_effect_count") != 0:
            raise ValueError(f"{label}: packet {packet_id} must not perform live apply or side effects")

        if packet.get("risk_tier") == "high":
            high_ready += 1
            expected_true = {
                "allowlist_match",
                "check_mode_passed",
                "rollback_plan_ready",
                "post_action_verifier_ready",
                "telegram_evidence_ready",
                "km_writeback_ready",
                "playbook_trust_writeback_ready",
                "controlled_executor_handoff_allowed",
            }
            missing = sorted(field for field in expected_true if packet.get(field) is not True)
            if missing:
                raise ValueError(f"{label}: high packet {packet_id} missing controlled executor gates: {missing}")
            if packet.get("owner_response_required") is not False:
                raise ValueError(f"{label}: high packet {packet_id} must not require owner response")
            if packet.get("break_glass_required") is not False:
                raise ValueError(f"{label}: high packet {packet_id} must not require break-glass")
            if packet.get("handoff_status") != "ready_for_controlled_executor":
                raise ValueError(f"{label}: high packet {packet_id} must be ready_for_controlled_executor")

        elif packet.get("risk_tier") == "critical":
            critical_break_glass += 1
            if packet.get("handoff_status") != "critical_break_glass_only":
                raise ValueError(f"{label}: critical packet {packet_id} must remain critical_break_glass_only")
            if packet.get("controlled_executor_handoff_allowed") is not False:
                raise ValueError(f"{label}: critical packet {packet_id} must not allow controlled executor handoff")
            if packet.get("owner_response_required") is not True or packet.get("break_glass_required") is not True:
                raise ValueError(f"{label}: critical packet {packet_id} must require owner response and break-glass")
        else:
            raise ValueError(f"{label}: packet {packet_id} risk_tier is invalid")

    if high_ready != 5 or critical_break_glass != 2:
        raise ValueError(f"{label}: expected high ready=5 and critical break-glass=2")


def _require_routes(payload: dict[str, Any], label: str) -> None:
    routes = payload.get("executor_routes") or []
    if len(routes) != 5:
        raise ValueError(f"{label}: executor_routes must contain 5 items")
    for route in routes:
        route_id = route.get("route_id")
        if route.get("route_status") != "ready_for_handoff":
            raise ValueError(f"{label}: route {route_id} must be ready_for_handoff")
        if route.get("live_apply_allowed_by_this_readback") is not False:
            raise ValueError(f"{label}: route {route_id} must not allow live apply from readback")
        if not route.get("required_inputs") or not route.get("blocked_actions"):
            raise ValueError(f"{label}: route {route_id} must list inputs and blocked actions")


def _require_verifier_bindings(payload: dict[str, Any], label: str) -> None:
    bindings = payload.get("verifier_bindings") or []
    if len(bindings) != 5:
        raise ValueError(f"{label}: verifier_bindings must contain 5 items")
    for binding in bindings:
        binding_id = binding.get("binding_id")
        if binding.get("required_before_dispatch") is not True:
            raise ValueError(f"{label}: binding {binding_id} must be required before dispatch")
        if binding.get("ready_count") != 5 or binding.get("blocked_count") != 0:
            raise ValueError(f"{label}: binding {binding_id} must have ready_count=5 and blocked_count=0")
        if not binding.get("failure_if_missing"):
            raise ValueError(f"{label}: binding {binding_id} failure_if_missing is required")


def _require_learning_contracts(payload: dict[str, Any], label: str) -> None:
    contracts = payload.get("learning_writeback_contracts") or []
    if len(contracts) != 3:
        raise ValueError(f"{label}: learning_writeback_contracts must contain 3 items")
    for contract in contracts:
        contract_id = contract.get("contract_id")
        if contract.get("writeback_status") != "ready_for_executor_receipt":
            raise ValueError(f"{label}: contract {contract_id} must be ready_for_executor_receipt")
        if contract.get("runtime_write_performed") is not False:
            raise ValueError(f"{label}: contract {contract_id} must not perform runtime write in readback")
        if not contract.get("required_fields"):
            raise ValueError(f"{label}: contract {contract_id} required_fields is required")


def _require_boundaries(payload: dict[str, Any], label: str) -> None:
    boundaries = payload.get("activation_boundaries") or {}
    missing = sorted(field for field in _TRUE_BOUNDARY_FLAGS if boundaries.get(field) is not True)
    if missing:
        raise ValueError(f"{label}: preview boundaries must remain true: {missing}")
    unsafe = sorted(field for field in _FALSE_BOUNDARY_FLAGS if boundaries.get(field) is not False)
    if unsafe:
        raise ValueError(f"{label}: live/write boundaries must remain false: {unsafe}")


def _require_redaction_contract(payload: dict[str, Any], label: str) -> None:
    contract = payload.get("display_redaction_contract") or {}
    if contract.get("redaction_required") is not True:
        raise ValueError(f"{label}: display redaction must be required")
    required_false = {
        "raw_tool_output_display_allowed",
        "raw_runtime_payload_display_allowed",
        "raw_telegram_payload_display_allowed",
        "private_reasoning_display_allowed",
        "secret_value_display_allowed",
        "work_window_transcript_display_allowed",
    }
    unsafe = sorted(field for field in required_false if contract.get(field) is not False)
    if unsafe:
        raise ValueError(f"{label}: display redaction fields must remain false: {unsafe}")


def _require_rollups(payload: dict[str, Any], label: str) -> None:
    rollups = payload.get("rollups") or {}
    sources = payload.get("source_readbacks") or []
    packets = payload.get("executor_handoff_packets") or []
    routes = payload.get("executor_routes") or []
    bindings = payload.get("verifier_bindings") or []
    learning_contracts = payload.get("learning_writeback_contracts") or []

    high_packets = [packet for packet in packets if packet.get("risk_tier") == "high"]
    critical_packets = [packet for packet in packets if packet.get("risk_tier") == "critical"]
    expected = {
        "source_readback_count": len(sources),
        "handoff_packet_count": len(packets),
        "ready_for_controlled_executor_count": sum(
            1 for packet in packets if packet.get("handoff_status") == "ready_for_controlled_executor"
        ),
        "critical_break_glass_count": sum(
            1 for packet in packets if packet.get("handoff_status") == "critical_break_glass_only"
        ),
        "high_risk_packet_count": len(high_packets),
        "critical_packet_count": len(critical_packets),
        "ansible_check_mode_packet_count": sum(1 for packet in packets if packet.get("executor_type") == "ansible_playbook"),
        "mcp_tool_route_count": sum(1 for packet in packets if packet.get("mcp_tool_ref")),
        "post_action_verifier_binding_count": sum(1 for packet in high_packets if packet.get("post_action_verifier_ready") is True),
        "telegram_evidence_binding_count": sum(1 for packet in high_packets if packet.get("telegram_evidence_ready") is True),
        "km_writeback_binding_count": sum(1 for packet in high_packets if packet.get("km_writeback_ready") is True),
        "playbook_trust_writeback_binding_count": sum(
            1 for packet in high_packets if packet.get("playbook_trust_writeback_ready") is True
        ),
        "owner_response_required_count": sum(1 for packet in packets if packet.get("owner_response_required") is True),
        "blocked_by_critical_boundary_count": len(critical_packets),
        "missing_check_mode_count": sum(1 for packet in high_packets if packet.get("check_mode_passed") is not True),
        "missing_rollback_count": sum(1 for packet in high_packets if packet.get("rollback_plan_ready") is not True),
        "missing_verifier_count": sum(1 for packet in high_packets if packet.get("post_action_verifier_ready") is not True),
        "missing_telegram_evidence_count": sum(1 for packet in high_packets if packet.get("telegram_evidence_ready") is not True),
        "missing_learning_writeback_count": sum(
            1
            for packet in high_packets
            if packet.get("km_writeback_ready") is not True
            or packet.get("playbook_trust_writeback_ready") is not True
        ),
        "executor_route_count": len(routes),
        "verifier_binding_count": len(bindings),
        "learning_writeback_contract_count": len(learning_contracts),
    }
    mismatches = sorted(field for field, value in expected.items() if rollups.get(field) != value)
    if mismatches:
        raise ValueError(f"{label}: rollup counts must match source arrays: {mismatches}")

    non_zero = sorted(field for field in _ZERO_ROLLUP_FIELDS if rollups.get(field) != 0)
    if non_zero:
        raise ValueError(f"{label}: live/write rollup counts must remain zero: {non_zero}")


def _require_no_forbidden_public_terms(payload: dict[str, Any], label: str) -> None:
    encoded = json.dumps(payload, ensure_ascii=False)
    hits = sorted(term for term in _FORBIDDEN_PUBLIC_TERMS if term in encoded)
    if hits:
        raise ValueError(f"{label}: forbidden public terms found: {hits}")


def _mismatches(payload: dict[str, Any], expected: dict[str, Any]) -> dict[str, Any]:
    return {
        key: {"expected": value, "actual": payload.get(key)}
        for key, value in expected.items()
        if payload.get(key) != value
    }
