from django import forms
from django.forms import BoundField

from . import widgets
from .slugify import django_slugify, slugify
from .validators import ValidateSlug


class SlugPreviewFormMixin:
    """
    Form Mixin to add necessary integration for the SlugPreviewField.
    This extends the default ``form[field]`` interface that produces the BoundField for HTML templates.
    """

    def __getitem__(self, item):
        boundfield = super().__getitem__(item)
        if isinstance(boundfield.field, SlugPreviewField):
            boundfield.__class__ = _upgrade_boundfield_class(boundfield.__class__)
        return boundfield


class BoundSlugField(BoundField):
    """
    An exta integration to pass information of the form to the widget.
    This is loaded via :class:`SlugPreviewFormMixin`
    """

    def as_widget(self, widget=None, attrs=None, only_initial=False):
        if not widget:
            widget = self.field.widget

        widget.instance = self.form.instance  # Widget needs ability to fill in the blanks.
        return super().as_widget(widget=widget, attrs=attrs, only_initial=only_initial)


UPGRADED_CLASSES = {}


def _upgrade_boundfield_class(cls):
    if cls is BoundField:
        return BoundSlugField
    elif issubclass(cls, BoundSlugField):
        return cls

    # When some other package also performs this same trick,
    # combine both classes on the fly. Avoid having to do that each time.
    # This is needed for django-parler
    try:
        return UPGRADED_CLASSES[cls]
    except KeyError:
        # Create once
        new_cls = type(f"BoundSlugField_{cls.__name__}", (cls, BoundSlugField), {})
        UPGRADED_CLASSES[cls] = new_cls
        return new_cls


class SlugPreviewModelForm(SlugPreviewFormMixin, forms.ModelForm):
    """
    A default model form, configured with the :class:`SlugPreviewFormMixin`.
    """


class SlugPreviewField(forms.SlugField):
    """
    A Form field that displays the slug and preview.
    It requires the :class:`SlugPreviewFormMixin` to be present in the form.
    """

    widget = widgets.SlugPreviewWidget

    def __init__(self, *args, **kwargs):
        self.populate_from = kwargs.pop("populate_from", None)
        self.always_update = kwargs.pop("always_update", False)
        self.url_format = kwargs.pop("url_format", None)
        self.slugify = kwargs.pop("slugify", slugify)

        if self.slugify != django_slugify:
            # This replaces the 'default_validators' setting at object level.
            self.default_validators = [ValidateSlug(self.slugify)]

        super().__init__(*args, **kwargs)
        self.widget.url_format = self.url_format

    def widget_attrs(self, widget):
        # Expose the form field settings to HTML
        attrs = super().widget_attrs(widget)
        attrs.update(
            {
                "data-populate-from": self.populate_from,
                "data-url-format": self.url_format,
                "data-always-update": self.always_update,
            }
        )
        return attrs
