"""
Base mixins for django-fsm state machines.

This module provides abstract model mixins that add convenience methods for
working with FSM-enabled models, including state inspection, transition
checking, and display helpers.

Key Features:
    - State value and display access
    - Transition availability checking
    - Rich choice metadata access
    - Consistent interface across all FSM models

Example Usage:
    Add the mixin to your FSM model::

        from django.db import models
        from apps.core.state_machine.mixins import StateMachineMixin
        from apps.core.state_machine.fields import RichFSMField

        class Park(StateMachineMixin, models.Model):
            state_field_name = 'status'  # Specify your FSM field name

            status = RichFSMField(
                choice_group='park_status',
                default='OPERATING'
            )

    Use the convenience methods::

        park = Park.objects.first()
        park.get_state_value()  # 'OPERATING'
        park.get_state_display_value()  # 'Operating'
        park.is_in_state('OPERATING')  # True
        park.can_transition('transition_to_closed_temp')  # True

See Also:
    - apps.core.state_machine.fields.RichFSMField: The FSM field implementation
    - django_fsm.can_proceed: FSM transition checking utility
"""

from collections.abc import Iterable
from typing import Any

from django.db import models
from django_fsm import can_proceed

# Default transition metadata for styling
TRANSITION_METADATA = {
    # Approval transitions
    "approve": {
        "style": "green",
        "icon": "check",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to approve this?",
    },
    "transition_to_approved": {
        "style": "green",
        "icon": "check",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to approve this?",
    },
    # Rejection transitions
    "reject": {
        "style": "red",
        "icon": "times",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to reject this?",
    },
    "transition_to_rejected": {
        "style": "red",
        "icon": "times",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to reject this?",
    },
    # Escalation transitions
    "escalate": {
        "style": "yellow",
        "icon": "arrow-up",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to escalate this?",
    },
    "transition_to_escalated": {
        "style": "yellow",
        "icon": "arrow-up",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to escalate this?",
    },
    # Assignment transitions
    "assign": {"style": "blue", "icon": "user-plus", "requires_confirm": False},
    "unassign": {"style": "gray", "icon": "user-minus", "requires_confirm": False},
    # Status transitions
    "start": {"style": "blue", "icon": "play", "requires_confirm": False},
    "complete": {
        "style": "green",
        "icon": "check-circle",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to complete this?",
    },
    "cancel": {
        "style": "red",
        "icon": "ban",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to cancel this?",
    },
    "reopen": {"style": "blue", "icon": "redo", "requires_confirm": False},
    # Resolution transitions
    "resolve": {
        "style": "green",
        "icon": "check-double",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to resolve this?",
    },
    "dismiss": {
        "style": "gray",
        "icon": "times-circle",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to dismiss this?",
    },
    # Default
    "default": {"style": "gray", "icon": "arrow-right", "requires_confirm": False},
}


def _get_transition_metadata(transition_name: str) -> dict[str, Any]:
    """Get metadata for a transition by name."""
    if transition_name in TRANSITION_METADATA:
        return TRANSITION_METADATA[transition_name].copy()

    for key, metadata in TRANSITION_METADATA.items():
        if key in transition_name.lower() or transition_name.lower() in key:
            return metadata.copy()

    return TRANSITION_METADATA["default"].copy()


def _format_transition_label(transition_name: str) -> str:
    """Format a transition method name into a human-readable label."""
    label = transition_name
    for prefix in ["transition_to_", "transition_", "do_"]:
        if label.startswith(prefix):
            label = label[len(prefix) :]
            break

    if label.endswith("ed") and len(label) > 3:
        if label.endswith("ied"):
            label = label[:-3] + "y"
        elif label[-3] == label[-4]:
            label = label[:-3]
        else:
            label = label[:-1]
            if not label.endswith("e"):
                label = label[:-1]

    return label.replace("_", " ").title()


class StateMachineMixin(models.Model):
    """
    Common helpers for models that use django-fsm.

    This abstract model mixin provides a consistent interface for working with
    FSM-enabled models, including methods for state inspection, transition
    checking, and display formatting.

    Class Attributes:
        state_field_name (str): The name of the FSM field on the model.
            Override this in subclasses if your field is not named 'state'.
            Default: 'state'

    Example:
        Basic usage with custom field name::

            class Ride(StateMachineMixin, models.Model):
                state_field_name = 'status'

                status = RichFSMField(...)

                @transition(field=status, source='OPERATING', target='SBNO')
                def transition_to_sbno(self, user=None):
                    pass

            ride = Ride.objects.first()

            # State inspection
            ride.get_state_value()  # 'OPERATING'
            ride.is_in_state('OPERATING')  # True
            ride.is_in_state('SBNO')  # False

            # Transition checking
            ride.can_transition('transition_to_sbno')  # True

            # Display formatting
            ride.get_state_display_value()  # 'Operating'

            # Rich choice access (when using RichFSMField)
            choice = ride.get_state_choice()
            choice.metadata  # {'icon': 'check', ...}

        Multiple FSM fields::

            class ComplexModel(StateMachineMixin, models.Model):
                status = RichFSMField(...)
                approval_status = RichFSMField(...)

                # Access non-default field
                model.get_state_value('approval_status')
    """

    state_field_name: str = "state"

    class Meta:
        abstract = True

    def get_state_value(self, field_name: str | None = None) -> Any:
        """Return the raw state value for the given field (default is `state`)."""
        name = field_name or self.state_field_name
        return getattr(self, name, None)

    def get_state_display_value(self, field_name: str | None = None) -> str:
        """Return the display label for the current state, if available."""
        name = field_name or self.state_field_name
        getter = getattr(self, f"get_{name}_display", None)
        if callable(getter):
            return getter()
        value = getattr(self, name, "")
        return value if value is not None else ""

    def get_state_choice(self, field_name: str | None = None):
        """Return the RichChoice object when the field provides one."""
        name = field_name or self.state_field_name
        getter = getattr(self, f"get_{name}_rich_choice", None)
        if callable(getter):
            return getter()
        return None

    def can_transition(self, transition_method_name: str) -> bool:
        """Check if a transition method can proceed for the current instance."""
        method = getattr(self, transition_method_name, None)
        if method is None or not callable(method):
            raise AttributeError(f"Transition method '{transition_method_name}' not found")
        return can_proceed(method)

    def get_available_transitions(self, field_name: str | None = None) -> Iterable[Any]:
        """Return available transitions when helpers are present."""
        name = field_name or self.state_field_name
        helper_name = f"get_available_{name}_transitions"
        helper = getattr(self, helper_name, None)
        if callable(helper):
            return helper()  # type: ignore[misc]
        return []

    def is_in_state(self, state: str, field_name: str | None = None) -> bool:
        """Convenience check for comparing the current state."""
        current_state = self.get_state_value(field_name)
        return current_state == state

    def get_available_user_transitions(self, user) -> list[dict[str, Any]]:
        """
        Get transitions available to the given user.

        This method returns a list of transition metadata dictionaries for
        transitions that the given user can execute based on their permissions.

        Args:
            user: The user to check permissions for

        Returns:
            List of dictionaries containing:
                - name: The transition method name
                - label: Human-readable label
                - icon: Font Awesome icon name
                - style: Button style (green, red, yellow, blue, gray)
                - requires_confirm: Whether confirmation is needed
                - confirm_message: Message to show in confirmation dialog

        Example:
            transitions = submission.get_available_user_transitions(request.user)
            for t in transitions:
                print(f"{t['label']}: {t['name']}")
        """
        transitions = []

        if not user:
            return transitions

        # Get available transitions from the FSM field
        available_transition_names = list(self.get_available_transitions())

        for transition_name in available_transition_names:
            method = getattr(self, transition_name, None)
            if method and callable(method):
                try:
                    if can_proceed(method, user):
                        metadata = _get_transition_metadata(transition_name)
                        transitions.append(
                            {
                                "name": transition_name,
                                "label": _format_transition_label(transition_name),
                                "icon": metadata.get("icon", "arrow-right"),
                                "style": metadata.get("style", "gray"),
                                "requires_confirm": metadata.get("requires_confirm", False),
                                "confirm_message": metadata.get("confirm_message", "Are you sure?"),
                            }
                        )
                except Exception:
                    # Skip transitions that raise errors
                    pass

        return transitions


__all__ = ["StateMachineMixin", "TRANSITION_METADATA"]
