"""Provider-neutral reasoning intent."""

from dataclasses import dataclass
from enum import StrEnum


class ReasoningControl(StrEnum):
    """Whether a request explicitly controls reasoning computation."""

    DEFAULT = "default"
    OFF = "off"
    ON = "on"


class ReasoningEffort(StrEnum):
    """Named reasoning effort understood at the FCC application boundary."""

    MINIMAL = "minimal"
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    XHIGH = "xhigh"
    MAX = "max"

    @property
    def budget_tokens(self) -> int:
        """Return FCC's numeric token budget for this effort."""

        return _EFFORT_BUDGET_TOKENS[self]


_EFFORT_BUDGET_TOKENS = {
    ReasoningEffort.MINIMAL: 512,
    ReasoningEffort.LOW: 512,
    ReasoningEffort.MEDIUM: 1_024,
    ReasoningEffort.HIGH: 2_048,
    ReasoningEffort.XHIGH: 4_096,
    ReasoningEffort.MAX: 8_192,
}


@dataclass(frozen=True, slots=True)
class ReasoningPolicy:
    """Resolved client and configuration intent passed to one provider.

    ``control`` and ``effort`` remain independent because clients may set an
    overall effort while separately disabling extended thinking. Providers
    translate the representable subset without changing the original intent.
    """

    control: ReasoningControl = ReasoningControl.DEFAULT
    effort: ReasoningEffort | None = None
    budget_tokens: int | None = None

    def __post_init__(self) -> None:
        if self.budget_tokens is not None and (
            not isinstance(self.budget_tokens, int)
            or isinstance(self.budget_tokens, bool)
            or self.budget_tokens <= 0
        ):
            raise ValueError("Reasoning budget must be a positive integer.")
        if self.budget_tokens is not None and self.control is not ReasoningControl.ON:
            raise ValueError("A reasoning budget requires reasoning control to be on.")

    @classmethod
    def provider_default(cls) -> ReasoningPolicy:
        """Leave reasoning computation to the provider."""

        return cls()

    @classmethod
    def off(cls) -> ReasoningPolicy:
        """Explicitly disable reasoning computation and output."""

        return cls(control=ReasoningControl.OFF)

    @classmethod
    def on(
        cls,
        *,
        effort: ReasoningEffort | None = None,
        budget_tokens: int | None = None,
    ) -> ReasoningPolicy:
        """Explicitly enable reasoning with optional client controls."""

        return cls(
            control=ReasoningControl.ON,
            effort=effort,
            budget_tokens=budget_tokens,
        )

    @property
    def output_enabled(self) -> bool:
        """Return whether provider reasoning may be exposed to the client."""

        return self.control is not ReasoningControl.OFF

    @property
    def requests_reasoning(self) -> bool:
        """Return whether the request explicitly asks the provider to reason."""

        return self.control is not ReasoningControl.OFF and (
            self.control is ReasoningControl.ON
            or self.effort is not None
            or self.budget_tokens is not None
        )

    @property
    def numeric_budget_tokens(self) -> int | None:
        """Express this intent as an exact or FCC-mapped numeric budget."""

        if self.control is ReasoningControl.OFF:
            return None
        if self.budget_tokens is not None:
            return self.budget_tokens
        if self.effort is None:
            return None
        return self.effort.budget_tokens


DEFAULT_REASONING_POLICY = ReasoningPolicy.provider_default()
