"""
P2-410 AI Agent action audit ledger snapshot.

Loads the latest committed action audit ledger. This module validates read-only
event templates and verifier receipt gates. It never writes audit DB rows,
timeline events, KM, PlayBook trust, Gateway queues, Telegram messages, secrets,
hosts, Kubernetes resources, or production state.
"""

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_action_audit_ledger_*.json"
_SCHEMA_VERSION = "ai_agent_action_audit_ledger_v1"
_RUNTIME_AUTHORITY = "agent_action_audit_ledger_no_live_write_committed_snapshot"
_EXPECTED_CURRENT_TASK = "P2-410"
_EXPECTED_NEXT_TASK = "P2-411"
_EXPECTED_SOURCE_SCHEMAS = {
    "ai_agent_low_medium_risk_whitelist_v1",
    "ai_agent_high_risk_owner_review_queue_v1",
    "ai_agent_task_result_audit_trail_v1",
    "awoooi_sre_digest_no_send_preview_v1",
    "awoooi_work_items_report_source_gap_owner_review_v1",
    "telegram_notification_egress_no_new_bypass_guard_v1",
    "governance_automation_inventory_readback_v1",
}
_TRUE_TRUTH_FLAGS = {
    "p2_408_whitelist_loaded",
    "p2_409_owner_queue_loaded",
    "p2_103_result_audit_loaded",
    "p2_110c_sre_digest_loaded",
    "p2_110e_work_items_loaded",
    "telegram_no_new_bypass_loaded",
    "audit_event_templates_ready",
    "verifier_receipt_gates_ready",
    "immutable_event_required",
    "redacted_evidence_refs_required",
    "read_only_mode",
}
_FALSE_TRUTH_FLAGS = {
    "audit_db_write_enabled",
    "timeline_write_enabled",
    "km_write_enabled",
    "playbook_trust_write_enabled",
    "gateway_queue_write_enabled",
    "telegram_send_enabled",
    "bot_api_call_enabled",
    "receipt_production_write_enabled",
    "production_write_enabled",
    "secret_read_enabled",
    "paid_api_call_enabled",
    "host_write_enabled",
    "kubectl_action_enabled",
    "destructive_operation_enabled",
}
_ZERO_TRUTH_COUNTS = {
    "audit_db_write_count_24h",
    "timeline_write_count_24h",
    "km_write_count_24h",
    "playbook_trust_write_count_24h",
    "gateway_queue_write_count_24h",
    "telegram_send_count_24h",
    "bot_api_call_count_24h",
    "receipt_production_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",
}
_FALSE_EVENT_FLAGS = {
    "audit_db_write_allowed",
    "timeline_write_allowed",
    "km_write_allowed",
    "playbook_trust_write_allowed",
    "gateway_queue_write_allowed",
    "telegram_send_allowed",
    "production_write_allowed",
}
_FALSE_BOUNDARY_FLAGS = _FALSE_TRUTH_FLAGS
_ZERO_ROLLUP_FIELDS = {
    "audit_db_write_count",
    "timeline_write_count",
    "km_write_count",
    "playbook_trust_write_count",
    "gateway_queue_write_count",
    "telegram_send_count",
    "bot_api_call_count",
    "receipt_production_write_count",
    "production_write_count",
    "secret_read_count",
    "paid_api_call_count",
    "host_write_count",
    "kubectl_action_count",
    "destructive_operation_count",
    "owner_response_received_count",
    "owner_response_accepted_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_action_audit_ledger(
    evaluations_dir: Path | None = None,
) -> dict[str, Any]:
    """Load the newest committed P2-410 action audit ledger snapshot."""
    directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
    candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
    if not candidates:
        raise FileNotFoundError(f"no AI Agent action audit ledger 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_audit_truth(payload, label)
    _require_audit_event_templates(payload, label)
    _require_verifier_receipt_gates(payload, label)
    _require_activation_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:
    if not payload.get("source_refs"):
        raise ValueError(f"{label}: source_refs must not be empty")
    sources = payload.get("source_readbacks") or []
    schemas = {item.get("source_schema_version") for item in sources}
    missing = sorted(_EXPECTED_SOURCE_SCHEMAS - schemas)
    if missing:
        raise ValueError(f"{label}: missing source schemas: {missing}")
    for item in sources:
        readback_id = item.get("readback_id") or "<missing>"
        for field in ("source_ref", "endpoint", "owner_agent", "status", "key_readback", "next_action"):
            if not item.get(field):
                raise ValueError(f"{label}: source readback {readback_id} missing {field}")


def _require_audit_truth(payload: dict[str, Any], label: str) -> None:
    truth = payload.get("audit_truth") or {}
    missing_true = sorted(flag for flag in _TRUE_TRUTH_FLAGS if truth.get(flag) is not True)
    if missing_true:
        raise ValueError(f"{label}: audit truth flags must remain true: {missing_true}")
    unsafe_false = sorted(flag for flag in _FALSE_TRUTH_FLAGS if truth.get(flag) is not False)
    if unsafe_false:
        raise ValueError(f"{label}: audit truth flags must remain false: {unsafe_false}")
    non_zero = sorted(field for field in _ZERO_TRUTH_COUNTS if truth.get(field) != 0)
    if non_zero:
        raise ValueError(f"{label}: audit truth counts must remain zero: {non_zero}")
    if not truth.get("truth_note"):
        raise ValueError(f"{label}: audit_truth.truth_note is required")


def _require_audit_event_templates(payload: dict[str, Any], label: str) -> None:
    events = payload.get("audit_event_templates") or []
    if not events:
        raise ValueError(f"{label}: audit_event_templates must not be empty")
    source_ids = {item.get("readback_id") for item in payload.get("source_readbacks") or []}
    risk_tiers = {event.get("risk_tier") for event in events}
    if not {"low", "medium", "high", "critical"}.issubset(risk_tiers):
        raise ValueError(f"{label}: audit event templates must cover low, medium, high, and critical")
    for event in events:
        event_id = event.get("audit_event_id") or "<missing>"
        if event.get("immutable_event_required") is not True:
            raise ValueError(f"{label}: event {event_id}.immutable_event_required must remain true")
        unsafe = sorted(flag for flag in _FALSE_EVENT_FLAGS if event.get(flag) is not False)
        if unsafe:
            raise ValueError(f"{label}: event {event_id} write/send flags must remain false: {unsafe}")
        if event.get("side_effect_count") != 0:
            raise ValueError(f"{label}: event {event_id}.side_effect_count must remain zero")
        for field in ("source_readback_ids", "required_audit_fields", "required_evidence_refs", "blocked_writes", "next_gate"):
            if not event.get(field):
                raise ValueError(f"{label}: event {event_id} missing {field}")
        missing_sources = sorted(set(event.get("source_readback_ids") or []) - source_ids)
        if missing_sources:
            raise ValueError(f"{label}: event {event_id} references missing source readbacks: {missing_sources}")


def _require_verifier_receipt_gates(payload: dict[str, Any], label: str) -> None:
    gates = payload.get("verifier_receipt_gates") or []
    if len(gates) < 1:
        raise ValueError(f"{label}: verifier_receipt_gates must not be empty")
    for gate in gates:
        gate_id = gate.get("gate_id") or "<missing>"
        if not gate.get("required_checks"):
            raise ValueError(f"{label}: verifier gate {gate_id} missing required_checks")
        if not gate.get("failure_if_missing"):
            raise ValueError(f"{label}: verifier gate {gate_id} missing failure_if_missing")
        for field in ("live_verifier_allowed", "receipt_write_allowed", "runtime_action_allowed"):
            if gate.get(field) is not False:
                raise ValueError(f"{label}: verifier gate {gate_id}.{field} must remain false")


def _require_activation_boundaries(payload: dict[str, Any], label: str) -> None:
    boundaries = payload.get("activation_boundaries") or {}
    required_true = {
        "committed_snapshot_read_allowed",
        "audit_event_template_preview_allowed",
        "verifier_receipt_gate_preview_allowed",
        "governance_ui_projection_allowed",
    }
    missing = sorted(field for field in required_true if boundaries.get(field) is not True)
    if missing:
        raise ValueError(f"{label}: activation 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}: activation boundaries must remain false: {unsafe}")


def _require_redaction_contract(payload: dict[str, Any], label: str) -> None:
    contract = payload.get("display_redaction_contract") or {}
    required_false = {
        "unsafe_payload_display_allowed",
        "private_reasoning_display_allowed",
        "secret_value_display_allowed",
        "raw_prompt_display_allowed",
        "work_window_transcript_display_allowed",
    }
    if contract.get("redaction_required") is not True:
        raise ValueError(f"{label}: redaction_required must remain true")
    unsafe = sorted(field for field in required_false if contract.get(field) is not False)
    if unsafe:
        raise ValueError(f"{label}: display redaction flags must remain false: {unsafe}")
    if not contract.get("allowed_display_fields") or not contract.get("blocked_display_fields"):
        raise ValueError(f"{label}: display redaction contract must list allowed and blocked fields")


def _require_rollups(payload: dict[str, Any], label: str) -> None:
    rollups = payload.get("rollups") or {}
    events = payload.get("audit_event_templates") or []
    gates = payload.get("verifier_receipt_gates") or []
    sources = payload.get("source_readbacks") or []
    expected_counts = {
        "source_readback_count": len(sources),
        "audit_event_template_count": len(events),
        "verifier_receipt_gate_count": len(gates),
        "low_medium_event_count": sum(1 for event in events if event.get("risk_tier") in {"low", "medium"}),
        "high_risk_event_count": sum(1 for event in events if event.get("risk_tier") == "high"),
        "critical_event_count": sum(1 for event in events if event.get("risk_tier") == "critical"),
        "report_gap_event_count": sum(
            1 for event in events if any("p2_110" in source for source in event.get("source_readback_ids") or [])
        ),
        "telegram_event_count": sum(
            1
            for event in events
            if any("telegram" in source for source in event.get("source_readback_ids") or [])
        ),
        "required_audit_field_count": sum(len(event.get("required_audit_fields") or []) for event in events),
        "blocked_runtime_action_count": len(
            {
                blocked
                for event in events
                for blocked in event.get("blocked_writes") or []
            }
        ),
    }
    mismatches = _mismatches(rollups, expected_counts)
    if mismatches:
        raise ValueError(f"{label}: rollup counts mismatch: {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/send rollups must remain zero: {non_zero}")


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


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