"""
AI Agent owner-approved fixture dry-run snapshot.

Loads the latest committed P2-403F fixture-only dry-run package. This module
never writes KM, updates PlayBook trust, writes timeline or replay scores,
writes Gateway queues, sends Telegram messages, opens Redis consumer groups,
starts workers, runs workflows, invokes secrets or paid APIs, or executes host
and cluster commands.
"""

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_owner_approved_fixture_dry_run_*.json"
_SCHEMA_VERSION = "ai_agent_owner_approved_fixture_dry_run_v1"
_RUNTIME_AUTHORITY = "owner_approved_fixture_dry_run_only_no_live_write"


def load_latest_ai_agent_owner_approved_fixture_dry_run(
    evaluations_dir: Path | None = None,
) -> dict[str, Any]:
    """Load the newest committed AI Agent owner-approved fixture dry-run package."""
    directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
    candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
    if not candidates:
        raise FileNotFoundError(
            f"no AI Agent owner-approved fixture dry-run 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_read_only_boundaries(payload, str(latest))
    _require_package_safety(payload, str(latest))
    _require_rollup_consistency(payload, str(latest))
    return payload


def _require_schema(payload: dict[str, Any], label: str) -> None:
    actual = payload.get("schema_version")
    if actual != _SCHEMA_VERSION:
        raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}, got {actual!r}")

    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 stay {_RUNTIME_AUTHORITY}")
    if status.get("current_task_id") != "P2-403F":
        raise ValueError(f"{label}: current_task_id must stay P2-403F")
    if status.get("next_task_id") != "P2-403G":
        raise ValueError(f"{label}: next_task_id must stay P2-403G")


def _require_read_only_boundaries(payload: dict[str, Any], label: str) -> None:
    boundaries = payload.get("approval_boundaries") or {}
    enabled = sorted(key for key, value in boundaries.items() if value is not False)
    if enabled:
        raise ValueError(f"{label}: approval boundaries must remain false: {enabled}")

    truth = payload.get("dry_run_truth") or {}
    if truth.get("owner_fixture_scope_approved") is not True:
        raise ValueError(f"{label}: owner fixture scope must be approved for fixture-only dry-run")
    if truth.get("fixture_dry_run_allowed") is not True:
        raise ValueError(f"{label}: fixture dry-run must remain explicitly allowed")

    false_flags = {
        "production_write_approved",
        "km_write_allowed",
        "playbook_trust_write_allowed",
        "timeline_learning_write_allowed",
        "agent_replay_score_write_allowed",
        "gateway_queue_write_allowed",
        "telegram_send_allowed",
        "redis_consumer_group_allowed",
        "db_migration_allowed",
        "workflow_trigger_allowed",
        "runtime_worker_allowed",
        "host_or_cluster_command_allowed",
        "secret_or_paid_api_allowed",
    }
    unsafe = sorted(flag for flag in false_flags if truth.get(flag) is not False)
    if unsafe:
        raise ValueError(f"{label}: dry-run truth flags must remain false: {unsafe}")

    zero_counts = {
        "live_learning_write_count",
        "live_playbook_trust_update_count",
        "live_km_update_count",
        "live_timeline_write_count",
        "live_replay_score_write_count",
        "live_gateway_queue_write_count",
        "live_telegram_send_count",
    }
    non_zero = sorted(key for key in zero_counts if truth.get(key) != 0)
    if non_zero:
        raise ValueError(f"{label}: live dry-run counts must remain zero: {non_zero}")


def _require_package_safety(payload: dict[str, Any], label: str) -> None:
    package = payload.get("fixture_package") or {}
    required_fields = set(package.get("required_fields") or [])
    required_minimum = {
        "fixture_event_id",
        "source_contract_ref",
        "scenario_type",
        "owner_scope_ref",
        "agent_owner",
        "target_surface",
        "proposed_delta_summary",
        "redacted_evidence_ref",
        "dry_run_expected_output",
        "no_write_proof_ref",
        "rollback_plan_ref",
    }
    missing = sorted(required_minimum - required_fields)
    if missing:
        raise ValueError(f"{label}: fixture package missing required fields: {missing}")
    if package.get("owner_review_required") is not True:
        raise ValueError(f"{label}: owner review must be required")
    if package.get("rollback_required") is not True:
        raise ValueError(f"{label}: rollback must be required")
    if package.get("no_write_proof_required") is not True:
        raise ValueError(f"{label}: no-write proof must be required")

    redaction = payload.get("display_redaction_contract") or {}
    if redaction.get("redaction_required") is not True:
        raise ValueError(f"{label}: frontend redaction must be required")
    for flag in (
        "raw_payload_display_allowed",
        "private_reasoning_display_allowed",
        "secret_value_display_allowed",
        "action_button_allowed",
    ):
        if redaction.get(flag) is not False:
            raise ValueError(f"{label}: {flag} must remain false")

    rollback = payload.get("rollback_contract") or {}
    if rollback.get("rollback_required") is not True:
        raise ValueError(f"{label}: rollback_contract.rollback_required must be true")
    if not rollback.get("rollback_steps"):
        raise ValueError(f"{label}: rollback steps must not be empty")
    if not payload.get("fixture_sets"):
        raise ValueError(f"{label}: fixture sets must not be empty")
    if not payload.get("simulation_steps"):
        raise ValueError(f"{label}: simulation steps must not be empty")


def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None:
    rollups = payload.get("rollups") or {}
    fixture_sets = payload.get("fixture_sets") or []
    gates = payload.get("dry_run_gates") or []
    steps = payload.get("simulation_steps") or []
    package = payload.get("fixture_package") or {}
    truth = payload.get("dry_run_truth") or {}
    expected_counts = {
        "fixture_set_count": len(fixture_sets),
        "dry_run_gate_count": len(gates),
        "simulation_step_count": len(steps),
        "approved_fixture_only_count": sum(
            1 for fixture in fixture_sets if fixture.get("status") == "approved_for_fixture_only"
        ),
        "blocked_runtime_action_count": len(
            {
                action
                for action in [gate.get("blocked_runtime_action") for gate in gates]
                if action
            }
        ),
        "required_field_count": len(package.get("required_fields") or []),
        "forbidden_field_count": len(package.get("forbidden_fields") or []),
    }
    mismatched = {
        key: {"expected": expected, "actual": rollups.get(key)}
        for key, expected in expected_counts.items()
        if rollups.get(key) != expected
    }
    if mismatched:
        raise ValueError(f"{label}: rollup counts must match payload sections: {mismatched}")

    approval_required = sorted(
        gate.get("gate_id") for gate in gates if gate.get("status") == "approval_required"
    )
    if sorted(rollups.get("approval_required_gate_ids") or []) != approval_required:
        raise ValueError(f"{label}: rollups.approval_required_gate_ids mismatch")

    live_write_total = sum(
        int(truth.get(key) or 0)
        for key in (
            "live_learning_write_count",
            "live_playbook_trust_update_count",
            "live_km_update_count",
            "live_timeline_write_count",
            "live_replay_score_write_count",
            "live_gateway_queue_write_count",
        )
    )
    if rollups.get("live_write_count_total") != live_write_total:
        raise ValueError(f"{label}: rollups.live_write_count_total mismatch")
    if rollups.get("live_send_count_total") != int(truth.get("live_telegram_send_count") or 0):
        raise ValueError(f"{label}: rollups.live_send_count_total mismatch")
    if rollups.get("live_receipt_count_total") != 0:
        raise ValueError(f"{label}: rollups.live_receipt_count_total must remain zero")
