"""
AI Agent matched PlayBook learning gap snapshot.

Loads the latest committed P2-104 matched PlayBook learning gap contract. This
module validates repo-committed evidence only; it never writes learning state,
updates PlayBook trust, writes KM / LOGBOOK / audit / timeline, writes Gateway
queues, sends Telegram messages, reads secrets, or starts runtime work.
"""

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_matched_playbook_learning_gap_*.json"
_SCHEMA_VERSION = "ai_agent_matched_playbook_learning_gap_v1"
_RUNTIME_AUTHORITY = "matched_playbook_learning_gap_contract_only_no_live_trust_write"


def load_latest_ai_agent_matched_playbook_learning_gap(
    evaluations_dir: Path | None = None,
) -> dict[str, Any]:
    """Load the newest committed AI Agent matched PlayBook learning gap contract."""
    directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
    candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
    if not candidates:
        raise FileNotFoundError(f"no AI Agent matched PlayBook learning gap 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")
    _require_schema(payload, str(latest))
    _require_production_readback(payload, str(latest))
    _require_learning_gap_truth(payload, str(latest))
    _require_gap_lanes(payload, str(latest))
    _require_learning_gates(payload, str(latest))
    _require_writeback_candidates(payload, str(latest))
    _require_redaction_contract(payload, str(latest))
    _require_rollup_consistency(payload, str(latest))
    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 {}
    if status.get("read_only_mode") is not True:
        raise ValueError(f"{label}: program_status.read_only_mode must be true")
    if status.get("runtime_authority") != _RUNTIME_AUTHORITY:
        raise ValueError(f"{label}: runtime_authority must remain {_RUNTIME_AUTHORITY}")
    if status.get("current_task_id") != "P2-104":
        raise ValueError(f"{label}: current_task_id must be P2-104")
    if status.get("next_task_id") != "P2-105":
        raise ValueError(f"{label}: next_task_id must be P2-105")


def _require_production_readback(payload: dict[str, Any], label: str) -> None:
    readback = payload.get("production_readback") or {}
    if readback.get("readback_mode") != "read_only_db_readback":
        raise ValueError(f"{label}: production_readback.readback_mode must remain read_only_db_readback")
    if readback.get("project_id_scope") != "awoooi":
        raise ValueError(f"{label}: production_readback.project_id_scope must remain awoooi")
    if readback.get("rls_fail_closed_verified") is not True:
        raise ValueError(f"{label}: production readback must verify RLS fail-closed")

    total = readback.get("approval_24h_total")
    matched = readback.get("approval_24h_matched")
    if not isinstance(total, int) or not isinstance(matched, int):
        raise ValueError(f"{label}: approval_24h_total and approval_24h_matched must be integers")
    if matched > total:
        raise ValueError(f"{label}: approval_24h_matched cannot exceed approval_24h_total")
    expected_rate = 0 if total == 0 else round((matched / total) * 100)
    if readback.get("matched_rate_24h_percent") != expected_rate:
        raise ValueError(f"{label}: matched_rate_24h_percent must match approval 24h readback")
    if matched != total:
        raise ValueError(f"{label}: P2-104 expects matched_playbook_id to be present for all 24h approvals")
    if readback.get("playbook_updated_24h") != 0:
        raise ValueError(f"{label}: playbook_updated_24h must remain 0 until trust write gate is approved")


def _require_learning_gap_truth(payload: dict[str, Any], label: str) -> None:
    truth = payload.get("learning_gap_truth") or {}
    required_true = {
        "p2_103_task_result_audit_loaded",
        "production_db_readback_completed",
        "rls_fail_closed_verified",
        "matched_playbook_id_present_24h",
        "matched_playbook_id_gap_resolved",
        "execution_learning_gap_detected",
        "approved_without_execution_meta_detected",
        "playbook_trust_update_gap_detected",
    }
    missing = sorted(field for field in required_true if truth.get(field) is not True)
    if missing:
        raise ValueError(f"{label}: learning gap readiness flags must remain true: {missing}")

    required_false = {
        "runtime_learning_write_enabled",
        "playbook_trust_write_enabled",
        "approval_auto_execute_enabled",
        "km_write_enabled",
        "logbook_runtime_write_enabled",
        "audit_db_write_enabled",
        "timeline_write_enabled",
        "gateway_queue_write_enabled",
        "telegram_send_enabled",
        "production_write_enabled",
        "secret_value_read_enabled",
        "destructive_operation_enabled",
        "work_window_transcript_display_allowed",
    }
    unsafe = sorted(field for field in required_false if truth.get(field) is not False)
    if unsafe:
        raise ValueError(f"{label}: live write/send/execution flags must remain false: {unsafe}")

    zero_counts = {
        "playbook_updated_24h",
        "live_learning_write_count_24h",
        "playbook_trust_write_count_24h",
        "gateway_queue_write_count_24h",
        "telegram_send_count_24h",
        "production_write_count_24h",
        "secret_value_read_count_24h",
        "destructive_operation_count_24h",
    }
    non_zero = sorted(field for field in zero_counts if truth.get(field) != 0)
    if non_zero:
        raise ValueError(f"{label}: live learning/trust/send/write counts must remain zero: {non_zero}")

    if truth.get("approval_24h_total") != truth.get("approval_24h_matched"):
        raise ValueError(f"{label}: matched_playbook_id gap must remain resolved for 24h approvals")
    if truth.get("approved_without_execution_meta_24h", 0) <= 0:
        raise ValueError(f"{label}: P2-104 must expose approved_without_execution_meta_24h as the active gap")


def _require_gap_lanes(payload: dict[str, Any], label: str) -> None:
    lanes = payload.get("gap_lanes") or []
    lane_ids = {lane.get("lane_id") for lane in lanes}
    required = {
        "lane_matched_id_present",
        "lane_approved_without_execution_meta",
        "lane_pending_human_gate",
        "lane_execution_failed_learning_candidate",
        "lane_playbook_trust_not_updated",
    }
    if lane_ids != required:
        raise ValueError(f"{label}: gap lanes must match {sorted(required)}")

    valid_statuses = {"passed", "blocked", "owner_review_required", "ready"}
    valid_risks = {"low", "medium", "high", "critical"}
    for lane in lanes:
        lane_id = lane.get("lane_id")
        if lane.get("status") not in valid_statuses:
            raise ValueError(f"{label}: lane {lane_id} status is invalid")
        if lane.get("risk_tier") not in valid_risks:
            raise ValueError(f"{label}: lane {lane_id} risk_tier is invalid")
        if lane.get("live_write_enabled") is not False:
            raise ValueError(f"{label}: lane {lane_id} live_write_enabled must remain false")
        for field in {"display_name", "owner_agent", "evidence", "next_gate"}:
            if not lane.get(field):
                raise ValueError(f"{label}: lane {lane_id} must list {field}")
        if not _is_redacted_sha256(lane.get("evidence_hash")):
            raise ValueError(f"{label}: lane {lane_id} must expose evidence_hash")


def _require_learning_gates(payload: dict[str, Any], label: str) -> None:
    gates = payload.get("learning_gates") or []
    gate_ids = {gate.get("gate_id") for gate in gates}
    required = {
        "gate_result_capture_contract",
        "gate_critic_reviewer_score",
        "gate_learning_writeback_approval",
        "gate_post_write_verifier",
        "gate_telegram_operator_receipt",
    }
    if gate_ids != required:
        raise ValueError(f"{label}: learning gates must match {sorted(required)}")
    for gate in gates:
        gate_id = gate.get("gate_id")
        if gate.get("status") not in {"ready", "needs_owner_review", "blocked_by_policy"}:
            raise ValueError(f"{label}: gate {gate_id} status is invalid")
        if gate.get("creates_runtime_write") is not False:
            raise ValueError(f"{label}: gate {gate_id} creates_runtime_write must remain false")
        if not gate.get("required_before") or not gate.get("failure_if_missing"):
            raise ValueError(f"{label}: gate {gate_id} must list required_before and failure_if_missing")


def _require_writeback_candidates(payload: dict[str, Any], label: str) -> None:
    candidates = payload.get("writeback_candidates") or []
    candidate_ids = {candidate.get("candidate_id") for candidate in candidates}
    required = {
        "candidate_approval_execution_bridge",
        "candidate_learning_service_payload",
        "candidate_playbook_trust_update",
        "candidate_operator_learning_report",
    }
    if candidate_ids != required:
        raise ValueError(f"{label}: writeback candidates must match {sorted(required)}")
    for candidate in candidates:
        candidate_id = candidate.get("candidate_id")
        if candidate.get("write_enabled") is not False:
            raise ValueError(f"{label}: candidate {candidate_id} write_enabled must remain false")
        if candidate.get("runtime_writer_enabled") is not False:
            raise ValueError(f"{label}: candidate {candidate_id} runtime_writer_enabled must remain false")
        if not candidate.get("required_fields"):
            raise ValueError(f"{label}: candidate {candidate_id} must list required_fields")
        if not candidate.get("blocker_summary"):
            raise ValueError(f"{label}: candidate {candidate_id} must list blocker_summary")
        if not _is_redacted_sha256(candidate.get("evidence_hash")):
            raise ValueError(f"{label}: candidate {candidate_id} must expose evidence_hash")


def _require_redaction_contract(payload: dict[str, Any], label: str) -> None:
    contract = payload.get("display_redaction_contract") or {}
    required_false = {
        "raw_prompt_display_allowed",
        "private_reasoning_display_allowed",
        "secret_value_display_allowed",
        "raw_telegram_payload_display_allowed",
        "work_window_transcript_display_allowed",
    }
    if contract.get("redaction_required") is not True:
        raise ValueError(f"{label}: display redaction must remain required")
    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_rollup_consistency(payload: dict[str, Any], label: str) -> None:
    rollups = payload.get("rollups") or {}
    truth = payload.get("learning_gap_truth") or {}
    readback = payload.get("production_readback") or {}
    lanes = payload.get("gap_lanes") or []
    gates = payload.get("learning_gates") or []
    candidates = payload.get("writeback_candidates") or []

    expected = {
        "gap_lane_count": len(lanes),
        "passed_lane_count": sum(1 for lane in lanes if lane.get("status") == "passed"),
        "blocked_lane_count": sum(1 for lane in lanes if lane.get("status") == "blocked"),
        "owner_review_lane_count": sum(1 for lane in lanes if lane.get("status") == "owner_review_required"),
        "approval_24h_total": readback.get("approval_24h_total"),
        "approval_24h_matched": readback.get("approval_24h_matched"),
        "matched_rate_24h_percent": readback.get("matched_rate_24h_percent"),
        "approved_without_execution_meta_24h": truth.get("approved_without_execution_meta_24h"),
        "pending_with_matched_24h": truth.get("pending_with_matched_24h"),
        "execution_failed_with_matched_24h": truth.get("execution_failed_with_matched_24h"),
        "playbook_with_execution_stats_count": readback.get("playbook_with_execution_stats"),
        "playbook_updated_24h_count": readback.get("playbook_updated_24h"),
        "learning_gate_count": len(gates),
        "writeback_candidate_count": len(candidates),
        "live_learning_write_count": truth.get("live_learning_write_count_24h"),
        "playbook_trust_write_count": truth.get("playbook_trust_write_count_24h"),
        "gateway_queue_write_count": truth.get("gateway_queue_write_count_24h"),
        "telegram_send_count": truth.get("telegram_send_count_24h"),
        "production_write_count": truth.get("production_write_count_24h"),
        "secret_value_read_count": truth.get("secret_value_read_count_24h"),
        "destructive_operation_count": truth.get("destructive_operation_count_24h"),
    }
    mismatches = {
        key: {"expected": expected_value, "actual": rollups.get(key)}
        for key, expected_value in expected.items()
        if rollups.get(key) != expected_value
    }
    if mismatches:
        raise ValueError(f"{label}: rollup counts mismatch: {mismatches}")


def _is_redacted_sha256(value: Any) -> bool:
    if not isinstance(value, str):
        return False
    if not value.startswith("sha256:") or len(value) != 71:
        return False
    return all(char in "0123456789abcdef" for char in value.removeprefix("sha256:"))
