"""
State machine fields with rich choice integration.

This module provides FSM field implementations that integrate with the rich
choice registry, enabling metadata-driven state machine definitions.

Key Features:
    - Automatic choice population from registry
    - Deprecated state handling
    - Rich choice metadata access on model instances
    - Migration support for custom field attributes

Example Usage:
    Define a model with a RichFSMField::

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

        class EditSubmission(models.Model):
            status = RichFSMField(
                choice_group='submission_status',
                domain='moderation',
                default='PENDING'
            )

    Access rich choice metadata on instances::

        submission = EditSubmission.objects.first()
        rich_choice = submission.get_status_rich_choice()
        print(rich_choice.metadata)  # {'is_actionable': True, ...}
        print(submission.get_status_display())  # "Pending Review"

    Define FSM transitions using django-fsm decorators::

        from django_fsm import transition

        class EditSubmission(models.Model):
            status = RichFSMField(...)

            @transition(field=status, source='PENDING', target='APPROVED')
            def transition_to_approved(self, user=None):
                self.handled_by = user
                self.handled_at = timezone.now()

See Also:
    - apps.core.choices.base.RichChoice: The choice object with metadata
    - apps.core.choices.registry: The central choice registry
    - apps.core.state_machine.mixins.StateMachineMixin: Convenience helpers
"""

from typing import Any

from django.core.exceptions import ValidationError
from django_fsm import FSMField as DjangoFSMField

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


class RichFSMField(DjangoFSMField):
    """
    FSMField that uses the rich choice registry for states.

    This field extends django-fsm's FSMField to integrate with the rich choice
    registry system, providing metadata-driven state machine definitions with
    automatic choice population and validation.

    The field automatically:
        - Populates choices from the registry based on choice_group and domain
        - Validates state values against the registry
        - Handles deprecated states appropriately
        - Adds convenience methods to the model class for accessing rich choice data

    Attributes:
        choice_group (str): Name of the choice group in the registry
        domain (str): Domain namespace for the choice group (default: "core")
        allow_deprecated (bool): Whether to allow deprecated states (default: False)

    Auto-generated Model Methods:
        - get_{field_name}_rich_choice(): Returns the RichChoice object for current state
        - get_{field_name}_display(): Returns the human-readable label

    Example:
        Basic field definition::

            class Ride(models.Model):
                status = RichFSMField(
                    choice_group='ride_status',
                    domain='core',
                    default='OPERATING',
                    max_length=30
                )

        Using auto-generated methods::

            ride = Ride.objects.get(pk=1)
            ride.status  # 'OPERATING'
            ride.get_status_display()  # 'Operating'
            ride.get_status_rich_choice()  # RichChoice(value='OPERATING', ...)
            ride.get_status_rich_choice().metadata  # {'icon': 'check', ...}

        With deprecated states (for historical data)::

            status = RichFSMField(
                choice_group='legacy_status',
                allow_deprecated=True  # Include deprecated choices
            )
    """

    def __init__(
        self,
        choice_group: str,
        domain: str = "core",
        max_length: int = 50,
        allow_deprecated: bool = False,
        **kwargs: Any,
    ):
        self.choice_group = choice_group
        self.domain = domain
        self.allow_deprecated = allow_deprecated

        if allow_deprecated:
            choices_list = registry.get_choices(choice_group, domain)
        else:
            choices_list = registry.get_active_choices(choice_group, domain)

        choices = [(choice.value, choice.label) for choice in choices_list]
        kwargs.setdefault("choices", choices)
        kwargs.setdefault("max_length", max_length)

        super().__init__(**kwargs)

    def validate(self, value: Any, model_instance: Any) -> None:
        """Validate the state value against the registry."""
        super().validate(value, model_instance)

        if value in (None, ""):
            return

        choice = registry.get_choice(self.choice_group, value, self.domain)
        if choice is None:
            raise ValidationError(f"'{value}' is not a valid state for {self.choice_group}")

        if choice.deprecated and not self.allow_deprecated:
            raise ValidationError(f"'{value}' is deprecated and cannot be used for new entries")

    def get_rich_choice(self, value: str) -> RichChoice | None:
        """Return the RichChoice object for a given state value."""
        return registry.get_choice(self.choice_group, value, self.domain)

    def get_choice_display(self, value: str) -> str:
        """Return the label for the given state value."""
        return registry.get_choice_display(self.choice_group, value, self.domain)

    def contribute_to_class(self, cls: Any, name: str, private_only: bool = False, **kwargs: Any) -> None:
        """Attach helpers to the model for convenience."""
        super().contribute_to_class(cls, name, private_only=private_only, **kwargs)

        def get_rich_choice_method(instance):
            state_value = getattr(instance, name)
            return self.get_rich_choice(state_value) if state_value else None

        setattr(cls, f"get_{name}_rich_choice", get_rich_choice_method)

        def get_display_method(instance):
            state_value = getattr(instance, name)
            return self.get_choice_display(state_value) if state_value else ""

        setattr(cls, f"get_{name}_display", get_display_method)

    def deconstruct(self):
        """Support Django migrations with custom init kwargs."""
        name, path, args, kwargs = super().deconstruct()
        kwargs["choice_group"] = self.choice_group
        kwargs["domain"] = self.domain
        kwargs["allow_deprecated"] = self.allow_deprecated
        return name, path, args, kwargs


__all__ = ["RichFSMField"]
