from django.shortcuts import get_object_or_404, redirect

from ..models import StaticPage
from ..utils import str_to_date

__all__ = ["GetDateMixin", "ParentMixin", "AttachedToMixin"]


class GetDateMixin:
    """Mixin offering utils to get date by `request.GET` or `kwargs['date']`"""

    date = None
    redirect_date_url = None

    def get_date(self, param="date"):
        date = self.request.GET.get(param)
        return str_to_date(date, "-") if date else self.kwargs[param] if param in self.kwargs else None

    def get(self, *args, **kwargs):
        if self.redirect_date_url and self.request.GET.get("date"):
            return redirect(
                self.redirect_date_url,
                date=self.request.GET["date"].replace("-", "/"),
            )

        self.date = self.get_date("date")
        return super().get(*args, **kwargs)


class ParentMixin:
    """Optional parent page for a list view.

    Parent is fetched and passed to the template context when
    `parent_model` is provided (queryset is filtered by parent page in
    such case).
    """

    parent_model = None
    """Parent model."""
    parent_url_kwarg = "parent_slug"
    """Url lookup argument."""
    parent_field = "slug"
    """Parent field for url lookup."""
    parent = None
    """Parent page object."""

    def get_parent(self, request, **kwargs):
        if self.parent_model is None or self.parent_url_kwarg not in kwargs:
            return

        lookup = {self.parent_field: kwargs[self.parent_url_kwarg]}
        return get_object_or_404(self.parent_model.objects.select_related("cover"), **lookup)

    def dispatch(self, request, *args, **kwargs):
        self.parent = self.get_parent(request, **kwargs)
        return super().dispatch(request, *args, **kwargs)

    def get_queryset(self):
        if self.parent is not None:
            return super().get_queryset().filter(parent=self.parent)
        return super().get_queryset()

    def get_context_data(self, **kwargs):
        parent = kwargs.setdefault("parent", self.parent)

        if parent is not None and parent.cover:
            kwargs.setdefault("cover", parent.cover.url)
        return super().get_context_data(**kwargs)


class AttachedToMixin:
    """Mixin for views that can have a static page attached to it."""

    attach_to_value = None
    """Value of StaticPage.attach_to."""

    def get_page(self):
        if self.attach_to_value is not None:
            return StaticPage.objects.filter(attach_to=self.attach_to_value).published().first()
        return super().get_page()


# FIXME: django-filter provides filter mixin, but I don't remember why this is
# used.
class FiltersMixin:
    """Mixin integrating Django filters' filter set."""

    filterset = None
    filterset_class = None

    def get_filterset(self, data, query):
        return self.filterset_class(data, query)

    def get_queryset(self):
        query = super().get_queryset()
        if self.filterset_class:
            self.filterset = self.get_filterset(self.request.GET.copy(), query)
            return self.filterset.qs
        return query

    def get_context_data(self, **kwargs):
        filterset = kwargs.setdefault("filterset", self.filterset)
        if filterset.is_valid():
            kwargs["filterset_data"] = filterset.form.cleaned_data
        else:
            kwargs["filterset_data"] = {}

        params = self.request.GET.copy()
        kwargs["get_params"] = params.pop("page", True) and params
        return super().get_context_data(**kwargs)


class VueFormDataMixin:
    """Provide form information as data to be used with vue components."""

    # Note: values corresponds to AFormSet expected one

    def get_form_items(self, formset):
        return [form.initial for form in formset.forms]

    def get_form_field_data(self, form, values=None):
        """Return form fields as data."""
        model = form.Meta.model
        fields = ((name, field, model._meta.get_field(name)) for name, field in form.base_fields.items())
        return [
            {
                "name": name,
                "label": str(m_field.verbose_name).capitalize(),
                "help": str(m_field.help_text).capitalize(),
                "hidden": field.widget.is_hidden,
                "value": values and values.get(name),
            }
            for name, field, m_field in fields
        ]

    def get_formset_data(self, formset, field_values=None, **kwargs):
        """Return formset as data object."""
        return {
            "prefix": formset.prefix,
            "management": {
                "initial_forms": formset.initial_form_count(),
                "min_num_forms": formset.min_num,
                "max_num_forms": formset.max_num,
            },
            "fields": self.get_form_field_data(formset.form, field_values),
            "initial_extra": formset.initial_extra and formset.initial_extra[0],
            "initials": self.get_form_items(formset),
            **kwargs,
        }
