"""
StateTransitionBuilder - Reads RichChoice metadata and generates FSM configurations.

This module provides utilities for building FSM transition configurations
from rich choice metadata, enabling declarative state machine definitions
where transitions are defined in the choice registry rather than code.

Key Features:
    - Extract valid transitions from choice metadata
    - Build complete transition graphs
    - Extract permission requirements for transitions
    - Identify terminal and actionable states
    - Generate consistent transition method names

Example Usage:
    Create a builder for a choice group::

        from apps.core.state_machine.builder import StateTransitionBuilder

        builder = StateTransitionBuilder(
            choice_group='submission_status',
            domain='moderation'
        )

        # Get valid transitions from a state
        targets = builder.extract_valid_transitions('PENDING')
        # Returns: ['APPROVED', 'REJECTED', 'ESCALATED']

        # Build complete transition graph
        graph = builder.build_transition_graph()
        # Returns: {
        #     'PENDING': ['APPROVED', 'REJECTED', 'ESCALATED'],
        #     'ESCALATED': ['APPROVED', 'REJECTED'],
        #     'APPROVED': [],  # Terminal state
        #     'REJECTED': [],  # Terminal state
        # }

        # Check state properties
        builder.is_terminal_state('APPROVED')  # True
        builder.is_actionable_state('PENDING')  # True

    Generate transition method names::

        from apps.core.state_machine.builder import determine_method_name_for_transition

        method = determine_method_name_for_transition('PENDING', 'APPROVED')
        # Returns: 'transition_to_approved'

Rich Choice Metadata Keys:
    The builder reads these metadata keys from RichChoice definitions:

    - can_transition_to (List[str]): Valid target states from this state
    - is_final (bool): Whether this is a terminal state
    - is_actionable (bool): Whether this state requires action
    - requires_moderator (bool): Whether moderator role is required
    - requires_admin_approval (bool): Whether admin role is required

See Also:
    - apps.core.choices.base.RichChoice: Choice definition with metadata
    - apps.core.choices.registry: Central choice registry
    - apps.core.state_machine.guards: Guard extraction from metadata
"""

from typing import Any

from django.core.exceptions import ImproperlyConfigured

from apps.core.choices.base import RichChoice
from apps.core.choices.registry import registry


class StateTransitionBuilder:
    """
    Reads RichChoice metadata and generates FSM transition configurations.

    This class provides a bridge between the rich choice registry and FSM
    configuration, extracting transition rules, permissions, and state
    properties from choice metadata.

    Attributes:
        choice_group (str): Name of the choice group in the registry
        domain (str): Domain namespace for the choice group
        choices: List of RichChoice objects for this group

    Example:
        Basic usage::

            builder = StateTransitionBuilder('ride_status', domain='core')

            # Get all states
            states = builder.get_all_states()
            # ['OPERATING', 'CLOSED_TEMP', 'SBNO', 'CLOSED_PERM', ...]

            # Get metadata for a state
            metadata = builder.get_choice_metadata('SBNO')
            # {'can_transition_to': ['OPERATING', 'CLOSED_PERM'], ...}

            # Check state properties
            builder.is_terminal_state('DEMOLISHED')  # True
            builder.is_terminal_state('SBNO')  # False

        Building transition decorators programmatically::

            builder = StateTransitionBuilder('park_status')
            graph = builder.build_transition_graph()

            for source_state, targets in graph.items():
                for target in targets:
                    method_name = determine_method_name_for_transition(
                        source_state, target
                    )
                    # Create transition method dynamically...
    """

    def __init__(self, choice_group: str, domain: str = "core"):
        """
        Initialize builder with a specific choice group.

        Args:
            choice_group: Name of the choice group in the registry
            domain: Domain namespace for the choice group

        Raises:
            ImproperlyConfigured: If choice group doesn't exist
        """
        self.choice_group = choice_group
        self.domain = domain
        self._cache: dict[str, Any] = {}

        # Validate choice group exists
        group = registry.get(choice_group, domain)
        if group is None:
            raise ImproperlyConfigured(f"Choice group '{choice_group}' not found in domain '{domain}'")

        self.choices = registry.get_choices(choice_group, domain)

    def get_choice_metadata(self, state_value: str) -> dict[str, Any]:
        """
        Retrieve metadata for a specific state.

        Args:
            state_value: The state value to get metadata for

        Returns:
            Dictionary containing the state's metadata
        """
        cache_key = f"metadata_{state_value}"
        if cache_key in self._cache:
            return self._cache[cache_key]

        choice = registry.get_choice(self.choice_group, state_value, self.domain)
        if choice is None:
            return {}

        metadata = choice.metadata.copy()
        self._cache[cache_key] = metadata
        return metadata

    def extract_valid_transitions(self, state_value: str) -> list[str]:
        """
        Get can_transition_to list from metadata.

        Args:
            state_value: The source state value

        Returns:
            List of valid target states
        """
        metadata = self.get_choice_metadata(state_value)
        transitions = metadata.get("can_transition_to", [])

        # Validate all target states exist
        for target in transitions:
            target_choice = registry.get_choice(self.choice_group, target, self.domain)
            if target_choice is None:
                raise ImproperlyConfigured(
                    f"State '{state_value}' references non-existent " f"transition target '{target}'"
                )

        return transitions

    def extract_permission_requirements(self, state_value: str) -> dict[str, bool]:
        """
        Extract permission requirements from metadata.

        Args:
            state_value: The state value to extract permissions for

        Returns:
            Dictionary with permission requirement flags
        """
        metadata = self.get_choice_metadata(state_value)
        return {
            "requires_moderator": metadata.get("requires_moderator", False),
            "requires_admin_approval": metadata.get("requires_admin_approval", False),
        }

    def is_terminal_state(self, state_value: str) -> bool:
        """
        Check if state is terminal (is_final flag).

        Args:
            state_value: The state value to check

        Returns:
            True if state is terminal/final
        """
        metadata = self.get_choice_metadata(state_value)
        return metadata.get("is_final", False)

    def is_actionable_state(self, state_value: str) -> bool:
        """
        Check if state is actionable (is_actionable flag).

        Args:
            state_value: The state value to check

        Returns:
            True if state is actionable
        """
        metadata = self.get_choice_metadata(state_value)
        return metadata.get("is_actionable", False)

    def build_transition_graph(self) -> dict[str, list[str]]:
        """
        Create a complete state transition graph.

        Returns:
            Dictionary mapping each state to its valid target states
        """
        cache_key = "transition_graph"
        if cache_key in self._cache:
            return self._cache[cache_key]

        graph = {}
        for choice in self.choices:
            transitions = self.extract_valid_transitions(choice.value)
            graph[choice.value] = transitions

        self._cache[cache_key] = graph
        return graph

    def get_all_states(self) -> list[str]:
        """
        Get all state values in the choice group.

        Returns:
            List of all state values
        """
        return [choice.value for choice in self.choices]

    def get_choice(self, state_value: str) -> RichChoice | None:
        """
        Get the RichChoice object for a state.

        Args:
            state_value: The state value to get

        Returns:
            RichChoice object or None if not found
        """
        return registry.get_choice(self.choice_group, state_value, self.domain)

    def clear_cache(self) -> None:
        """Clear the internal cache."""
        self._cache.clear()


def determine_method_name_for_transition(source: str, target: str) -> str:
    """
    Determine appropriate method name for a transition.

    Always uses transition_to_<state> pattern to avoid conflicts with
    business logic methods (approve, reject, escalate, etc.).

    Args:
        source: Source state
        target: Target state

    Returns:
        Method name in format "transition_to_{target_lower}"
    """
    # Always use transition_to_<state> pattern to avoid conflicts
    # with business logic methods
    return f"transition_to_{target.lower()}"


__all__ = ["StateTransitionBuilder", "determine_method_name_for_transition"]
