"""
Django Model Fields for Rich Choices

This module provides Django model field implementations for rich choice objects.
"""

from typing import Any

from django.core.exceptions import ValidationError
from django.db import models
from django.forms import ChoiceField

from .base import RichChoice
from .registry import registry


class RichChoiceField(models.CharField):
    """
    Django model field for rich choice objects.

    This field stores the choice value as a CharField but provides
    rich choice functionality through the registry system.
    """

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

        Args:
            choice_group: Name of the choice group in the registry
            domain: Domain namespace for the choice group
            max_length: Maximum length for the stored value
            allow_deprecated: Whether to allow deprecated choices
            **kwargs: Additional arguments passed to CharField
        """
        self.choice_group = choice_group
        self.domain = domain
        self.allow_deprecated = allow_deprecated

        # Set choices from registry for Django admin and forms
        if self.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["choices"] = choices
        kwargs["max_length"] = max_length

        super().__init__(**kwargs)

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

        if value is None or value == "":
            return

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

        # Check if deprecated choices are allowed
        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:
        """Get the RichChoice object for a value"""
        return registry.get_choice(self.choice_group, value, self.domain)

    def get_choice_display(self, value: str) -> str:
        """Get the display label for a choice 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:
        """Add helper methods to the model class (signature compatible with Django Field)"""
        super().contribute_to_class(cls, name, private_only=private_only, **kwargs)

        # Add get_FOO_rich_choice method
        def get_rich_choice_method(instance):
            value = getattr(instance, name)
            return self.get_rich_choice(value) if value else None

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

        # Add get_FOO_display method (Django provides this, but we enhance it)
        def get_display_method(instance):
            value = getattr(instance, name)
            return self.get_choice_display(value) if value else ""

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

    def deconstruct(self):
        """Support for Django migrations"""
        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


class RichChoiceFormField(ChoiceField):
    """
    Form field for rich choices with enhanced functionality.
    """

    def __init__(
        self,
        choice_group: str,
        domain: str = "core",
        allow_deprecated: bool = False,
        show_descriptions: bool = False,
        **kwargs,
    ):
        """
        Initialize the form field.

        Args:
            choice_group: Name of the choice group in the registry
            domain: Domain namespace for the choice group
            allow_deprecated: Whether to allow deprecated choices
            show_descriptions: Whether to show descriptions in choice labels
            **kwargs: Additional arguments passed to ChoiceField
        """
        self.choice_group = choice_group
        self.domain = domain
        self.allow_deprecated = allow_deprecated
        self.show_descriptions = show_descriptions

        # Get choices from registry
        if allow_deprecated:
            choices_list = registry.get_choices(choice_group, domain)
        else:
            choices_list = registry.get_active_choices(choice_group, domain)

        # Format choices for display
        choices = []
        for choice in choices_list:
            label = choice.label
            if show_descriptions and choice.description:
                label = f"{choice.label} - {choice.description}"
            choices.append((choice.value, label))

        kwargs["choices"] = choices
        super().__init__(**kwargs)

    def validate(self, value: Any) -> None:
        """Validate the choice value"""
        super().validate(value)

        if value is None or value == "":
            return

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

        # Check if deprecated choices are allowed
        if choice.deprecated and not self.allow_deprecated:
            raise ValidationError(f"'{value}' is deprecated and cannot be used")


def create_rich_choice_field(
    choice_group: str, domain: str = "core", max_length: int = 50, allow_deprecated: bool = False, **kwargs
) -> RichChoiceField:
    """
    Factory function to create a RichChoiceField.

    This is useful for creating fields with consistent settings
    across multiple models.
    """
    return RichChoiceField(
        choice_group=choice_group, domain=domain, max_length=max_length, allow_deprecated=allow_deprecated, **kwargs
    )
