"""Constrained execution helpers for policy-bound transition usage."""

from __future__ import annotations

from collections.abc import Iterable, Mapping
from dataclasses import dataclass
from datetime import datetime
from typing import Any
from uuid import UUID

from .evidence import AuthoritySource, normalise_authority_sources
from .transition import Transition, TransitionResult

__all__ = ["AgentExecutionContext", "AgentPolicyEnforcement"]


@dataclass(frozen=True, slots=True)
class AgentPolicyEnforcement:
    """Policy enforcement toggles for constrained transition execution."""

    require_profile: bool = False
    require_policy_version: bool = False
    minimum_authority_sources: int = 0

    def __post_init__(self) -> None:
        if not isinstance(self.require_profile, bool):
            msg = "require_profile must be a boolean."
            raise TypeError(msg)
        if not isinstance(self.require_policy_version, bool):
            msg = "require_policy_version must be a boolean."
            raise TypeError(msg)
        if (
            not isinstance(self.minimum_authority_sources, int)
            or self.minimum_authority_sources < 0
        ):
            msg = "minimum_authority_sources must be an integer greater than or equal to 0."
            raise ValueError(msg)


class AgentExecutionContext:
    """Deterministic hard-fail execution wrapper for transition application."""

    __slots__ = ("_enforcement", "_transition")

    def __init__(
        self,
        transition: Transition,
        *,
        enforcement: AgentPolicyEnforcement | None = None,
    ) -> None:
        if not isinstance(transition, Transition):
            msg = "transition must be a Transition instance."
            raise TypeError(msg)
        self._transition = transition
        self._enforcement = enforcement if enforcement is not None else AgentPolicyEnforcement()

    @property
    def transition(self) -> Transition:
        """Return wrapped transition."""
        return self._transition

    @property
    def enforcement(self) -> AgentPolicyEnforcement:
        """Return enforcement configuration."""
        return self._enforcement

    def apply(
        self,
        *,
        state: Mapping[str, Any],
        entity_type: str,
        entity_id: str,
        actor: str,
        authority: str | None = None,
        authority_sources: Iterable[AuthoritySource | Mapping[str, Any]] | None = None,
        profile: str | None = None,
        policy_version: str | None = None,
        authority_response: Mapping[str, Any] | None = None,
        transition_id: str | UUID | None = None,
        timestamp: datetime | str | None = None,
    ) -> TransitionResult:
        """Apply wrapped transition with strict policy-flag enforcement."""
        self._enforce_policy_flags(
            profile=profile,
            policy_version=policy_version,
            authority_sources=authority_sources,
        )
        return self._transition.apply(
            state=state,
            entity_type=entity_type,
            entity_id=entity_id,
            actor=actor,
            authority=authority,
            authority_sources=authority_sources,
            profile=profile,
            policy_version=policy_version,
            authority_response=authority_response,
            transition_id=transition_id,
            timestamp=timestamp,
        )

    def _enforce_policy_flags(
        self,
        *,
        profile: str | None,
        policy_version: str | None,
        authority_sources: Iterable[AuthoritySource | Mapping[str, Any]] | None,
    ) -> None:
        if self._enforcement.require_profile:
            if not self._transition.supports_policy_profiles:
                msg = "require_profile enforcement requires a profile-registry transition."
                raise ValueError(msg)
            if profile is None:
                msg = "AgentExecutionContext requires explicit profile."
                raise ValueError(msg)
        if self._enforcement.require_policy_version:
            if not self._transition.supports_policy_profiles:
                msg = "require_policy_version enforcement requires a profile-registry transition."
                raise ValueError(msg)
            if policy_version is None:
                msg = "AgentExecutionContext requires explicit policy_version."
                raise ValueError(msg)
        normalised_sources = normalise_authority_sources(authority_sources)
        minimum = self._enforcement.minimum_authority_sources
        if len(normalised_sources) < minimum:
            msg = f"AgentExecutionContext requires at least {minimum} authority source(s)."
            raise ValueError(msg)
