from __future__ import annotations

from dataclasses import asdict, dataclass
from enum import Enum
from typing import Any, Dict, Optional

from .llm_middleware import MiddlewareConfig


class ProtectMode(str, Enum):
    """
    Public modes for the drop-in reliability runtime.

    These are intentionally coarse-grained. The goal is:
    - 1 flag users can understand
    - deterministic defaults
    - explicit tradeoffs: cost vs safety vs latency
    """

    BALANCED = "balanced"
    SAFE = "safe"
    CHEAP = "cheap"
    BENCHMARK = "benchmark"


@dataclass(frozen=True)
class Policy:
    mode: ProtectMode

    def to_middleware_config(self) -> MiddlewareConfig:
        """
        Map a mode to deterministic MiddlewareConfig defaults.

        This is the v0 "policy engine": it expresses rules by:
        - mode (explicit)
        - risk (entropy thresholds)
        - cost (context pruning + retries)
        """
        m = self.mode

        if m == ProtectMode.SAFE:
            return MiddlewareConfig(
                high_entropy_threshold=0.55,
                low_entropy_threshold=0.20,
                hallucination_threshold=0.50,
                enable_prompt_injection=True,
                enable_temperature_control=True,
                enable_context_pruning=True,
                enable_hallucination_detection=True,
                enable_cost_optimization=True,
                enable_retry_logic=True,
                max_context_tokens=3500,
                max_retries=4,
                retry_temperature_decay=0.75,
                enable_transport_retries=True,
                transport_max_retries=6,
                transport_backoff_base_s=0.35,
                transport_backoff_max_s=10.0,
                transport_jitter_s=0.20,
                enable_cost_estimation=True,
                enable_evidence_audit=False,
            )

        if m == ProtectMode.CHEAP:
            return MiddlewareConfig(
                high_entropy_threshold=0.75,
                low_entropy_threshold=0.15,
                hallucination_threshold=0.70,
                enable_prompt_injection=False,
                enable_temperature_control=True,
                enable_context_pruning=True,
                enable_hallucination_detection=False,
                enable_cost_optimization=True,
                enable_retry_logic=False,  # retries cost tokens and latency
                max_context_tokens=2500,
                max_retries=0,
                retry_temperature_decay=0.80,
                enable_transport_retries=True,
                transport_max_retries=3,
                transport_backoff_base_s=0.20,
                transport_backoff_max_s=6.0,
                transport_jitter_s=0.10,
                enable_cost_estimation=True,
                enable_evidence_audit=False,
            )

        if m == ProtectMode.BENCHMARK:
            # Make runs stable/reproducible: keep everything on, but reduce randomness.
            return MiddlewareConfig(
                high_entropy_threshold=0.60,
                low_entropy_threshold=0.20,
                hallucination_threshold=0.55,
                enable_prompt_injection=True,
                enable_temperature_control=True,
                enable_context_pruning=False,  # avoid confounding benchmark scores
                enable_hallucination_detection=True,
                enable_cost_optimization=False,
                enable_retry_logic=True,
                max_context_tokens=4000,
                max_retries=3,
                retry_temperature_decay=0.80,
                enable_transport_retries=True,
                transport_max_retries=4,
                transport_backoff_base_s=0.25,
                transport_backoff_max_s=8.0,
                transport_jitter_s=0.15,
                enable_cost_estimation=True,
                enable_evidence_audit=True,  # enables groundedness metrics in eval-style prompts
            )

        # BALANCED (default)
        return MiddlewareConfig(
            high_entropy_threshold=0.70,
            low_entropy_threshold=0.20,
            hallucination_threshold=0.60,
            enable_prompt_injection=True,
            enable_temperature_control=True,
            enable_context_pruning=True,
            enable_hallucination_detection=True,
            enable_cost_optimization=True,
            enable_retry_logic=True,
            max_context_tokens=4000,
            max_retries=3,
            retry_temperature_decay=0.80,
            enable_transport_retries=True,
            transport_max_retries=4,
            transport_backoff_base_s=0.25,
            transport_backoff_max_s=8.0,
            transport_jitter_s=0.15,
            enable_cost_estimation=True,
            enable_evidence_audit=False,
        )


def parse_mode(mode: Optional[str]) -> ProtectMode:
    s = (mode or "").strip().lower()
    if not s:
        return ProtectMode.BALANCED
    for m in ProtectMode:
        if s == m.value:
            return m
    raise ValueError(f"Unknown mode: {mode!r}. Expected one of: {[m.value for m in ProtectMode]}")


def apply_overrides(cfg: MiddlewareConfig, overrides: Dict[str, Any]) -> MiddlewareConfig:
    if not overrides:
        return cfg
    d = asdict(cfg)
    for k, v in overrides.items():
        if k not in d:
            continue
        d[k] = v
    return MiddlewareConfig(**d)
