from django import forms
from django.core.exceptions import ValidationError
from django.db import transaction
from django.utils.translation import gettext_lazy as _

from apps.common.widgets.tom_select import TomSelect, TomSelectMultiple
from apps.common.middleware.thread_local import get_current_user


class DynamicModelChoiceField(forms.ModelChoiceField):
    def __init__(self, model, *args, **kwargs):
        self.model = model
        self.to_field_name = kwargs.pop("to_field_name", "pk")

        self.create_field = kwargs.pop("create_field", None)

        self.queryset = kwargs.pop("queryset", model.objects.all())

        self.widget = TomSelect(clear_button=True, create=True)

        super().__init__(queryset=self.queryset, *args, **kwargs)
        self._created_instance = None

    def to_python(self, value):
        if value in self.empty_values:
            return None
        try:
            return self.model.objects.get(**{self.to_field_name: value})
        except (ValueError, TypeError, self.model.DoesNotExist):
            return value  # Return the raw value; we'll handle creation in clean()

    def clean(self, value):
        if value in self.empty_values:
            if self.required:
                raise ValidationError(self.error_messages["required"], code="required")
            return None

        if isinstance(value, self.model):
            return value

        if isinstance(value, str):
            value = value.strip()
            if not value:
                if self.required:
                    raise ValidationError(
                        self.error_messages["required"], code="required"
                    )
                return None

            try:
                if value.isdigit():
                    return self.model.objects.get(id=value)
                else:
                    raise self.model.DoesNotExist
            except self.model.DoesNotExist:
                if self.create_field:
                    try:
                        with transaction.atomic():
                            # First try to get the object
                            lookup = {self.create_field: value}
                            try:
                                instance = self.model.objects.get(**lookup)
                            except self.model.DoesNotExist:
                                # Create a new instance directly
                                instance = self.model(**lookup)
                                instance.save()

                            self._created_instance = instance
                            return instance
                    except Exception as e:
                        raise ValidationError(_("Error creating new instance"))
                else:
                    raise ValidationError(
                        self.error_messages["invalid_choice"], code="invalid_choice"
                    )

        return super().clean(value)

    def bound_data(self, data, initial):
        if self._created_instance and isinstance(data, str):
            if data == self._created_instance.name:
                return self._created_instance.pk
        return super().bound_data(data, initial)


class DynamicModelMultipleChoiceField(forms.ModelMultipleChoiceField):
    """
    A custom ModelMultipleChoiceField that creates new entries if they don't exist.

    This field allows users to select multiple existing options or add new ones.
    If a selected option doesn't exist, it will be created in the database.

    Attributes:
        create_field (str): The name of the field to use when creating new instances.
    """

    def __init__(self, model, **kwargs):
        """
        Args:
            create_field (str): The name of the field to use when creating new instances.
            *args: Variable length argument list.
            **kwargs: Arbitrary keyword arguments.
        """
        self.create_field = kwargs.pop("create_field", None)
        if not self.create_field:
            raise ValueError("The 'create_field' parameter is required.")
        self.model = model
        self.queryset = kwargs.pop("queryset", model.objects.all())
        super().__init__(queryset=self.queryset, **kwargs)

        self.widget = TomSelectMultiple(
            remove_button=True, clear_button=True, create=True, checkboxes=True
        )

    def _create_new_instance(self, value):
        """
        Create a new instance of the model with the given value.

        Args:
            value: The value to use for creating the new instance.

        Returns:
            Model: The newly created model instance.

        Raises:
            ValidationError: If there's an error creating the new instance.
        """
        try:
            with transaction.atomic():
                # Check if exists first without using update_or_create
                lookup = {self.create_field: value}
                try:
                    # Use base manager to bypass distinct filters
                    instance = self.model.objects.get(**lookup)
                    return instance
                except self.model.DoesNotExist:
                    # Create a new instance directly
                    instance = self.model(**lookup)
                    instance.save()
                    return instance
        except Exception as e:
            raise ValidationError(_("Error creating new instance"))

    def clean(self, value):
        if not value:
            return []

        string_values = set(str(v) for v in value)

        # Get existing objects first
        existing_objects = list(
            self.queryset.filter(**{f"{self.create_field}__in": string_values})
        )
        existing_values = set(
            str(getattr(obj, self.create_field)) for obj in existing_objects
        )

        # Create new objects for missing values
        new_values = string_values - existing_values
        new_objects = []

        for new_value in new_values:
            new_objects.append(self._create_new_instance(new_value))

        return existing_objects + new_objects
