"""Policy engine orchestrating design decisions."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Iterable, Mapping, Protocol

from .rules import DEFAULT_POLICY, Policy, load_policy
from .targets import PolicyTarget, TargetRegistry


class PolicyProvider(Protocol):
    """Interface for objects that provide policy decisions for specific domains."""

    def supports(self, target: PolicyTarget) -> bool:
        """Return True if this provider handles the given target."""
        ...

    def evaluate(
        self, target: PolicyTarget, options: Mapping[str, Any]
    ) -> Mapping[str, Any]:
        """Evaluate policy options and return resolved values."""
        ...


@dataclass(slots=True)
class PolicyContext:
    """Holds evaluated policy options for quick lookup."""

    selections: dict[str, Mapping[str, Any]] = field(default_factory=dict)

    def get(
        self, target: str, default: Mapping[str, Any] | None = None
    ) -> Mapping[str, Any] | None:
        """Get evaluated options for a target."""
        return self.selections.get(target, default)

    def get_option(self, target: str, key: str, default: Any = None) -> Any:
        """Get a specific option value from a target."""
        target_options = self.selections.get(target, {})
        return target_options.get(key, default)


class PolicyEngine:
    """Evaluate policies for registered targets using pluggable providers.

    Example:
        engine = PolicyEngine()
        engine.set_policy("accessible")

        # Register custom providers
        engine.register_provider(TypographyProvider())
        engine.register_provider(ColorProvider())

        # Evaluate all policies
        context = engine.evaluate()

        # Use evaluated options
        base_size = context.get_option("typography", "base_size_pt")
    """

    def __init__(
        self,
        *,
        providers: Iterable[PolicyProvider] | None = None,
        default_policy: Policy | None = None,
        target_registry: TargetRegistry | None = None,
    ) -> None:
        self._providers = list(providers or [])
        self._policy = default_policy or DEFAULT_POLICY
        self._targets = target_registry or TargetRegistry.default()

    @property
    def policy(self) -> Policy:
        """Current policy."""
        return self._policy

    def register_provider(self, provider: PolicyProvider) -> None:
        """Register a policy provider."""
        self._providers.append(provider)

    def set_policy(self, policy_or_name: str | Policy) -> None:
        """Set the active policy by name or Policy object."""
        if isinstance(policy_or_name, str):
            self._policy = load_policy(policy_or_name)
        else:
            self._policy = policy_or_name

    def evaluate(self, *targets: PolicyTarget) -> PolicyContext:
        """Evaluate policies for the given targets (or all if none specified)."""
        if not targets:
            targets = tuple(self._targets.iter_targets())

        selections: dict[str, Mapping[str, Any]] = {}
        options = self._policy.options

        for target in targets:
            # Get base options for this target
            target_options = options.get(target.name, {})

            # Let providers refine the options
            result = self._evaluate_target(target, target_options)
            if result is not None:
                selections[target.name] = result
            else:
                # Use base options if no provider handled it
                selections[target.name] = target_options

        return PolicyContext(selections=selections)

    def _evaluate_target(
        self,
        target: PolicyTarget,
        options: Mapping[str, Any],
    ) -> Mapping[str, Any] | None:
        """Evaluate a single target using registered providers."""
        for provider in self._providers:
            if provider.supports(target):
                return provider.evaluate(target, options)
        return None


__all__ = ["PolicyContext", "PolicyEngine", "PolicyProvider"]
