"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 12.01.22

"""
import django_filters
from django import forms
from django.db.models import QuerySet, Q
from django.utils.translation import gettext_lazy as _
from dal_select2.widgets import ModelSelect2

from codelist.models import KonovaCode
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID
from intervention.inputs import DummyFilterInput
from konova.models import Parcel, District


class KeywordTableFilterMixin(django_filters.FilterSet):
    q = django_filters.Filter(
        method='filter_by_keyword',
        # Since we use a custom search bar in the template, we need to 'render' this filter
        # as 'anonymous' HiddenInput (no id, no name). This way our custom search bar's id and name won't be
        # overwritten with these id and name (which would be equal)
        # This way we can use the simple filter method mapping for a parameter without using a predefined widget!
        widget=DummyFilterInput(),
    )

    class Meta:
        abstract = True

    def filter_by_keyword(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value of search bar input

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        value = value.strip()
        # build filter expression
        q = Q(title__icontains=value) | Q(identifier__icontains=value)
        return queryset.filter(q)


class FileNumberTableFilterMixin(django_filters.FilterSet):
    rf = django_filters.CharFilter(
        method="filter_file_number",
        label=_(""),
        label_suffix=_(""),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("File number"),
                "title": _("Search for file number"),
                "class": "form-control",
            }
        ),
    )

    def filter_file_number(self, queryset, name, value) -> QuerySet:
        queryset = queryset.filter(
            Q(responsible__registration_file_number__icontains=value) |
            Q(responsible__conservation_file_number__icontains=value)
        )
        return queryset


class GeoReferencedTableFilterMixin(django_filters.FilterSet):
    """ A mixin for AbstractTableFilter

    Specialized on filtering GeoReferenced model types

    """
    # Parcel gmrkng
    di = django_filters.CharFilter(
        method="filter_district",
        label=_(""),
        label_suffix=_(""),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("District"),
                "title": _("Search for district"),
                "class": "form-control",
            }
        ),
    )
    # Parcel gmrkng
    pg = django_filters.CharFilter(
        method="filter_gmrkng",
        label=_(""),
        label_suffix=_(""),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("Parcel gmrkng"),
                "title": _("Search for parcel gmrkng"),
                "class": "form-control",
            }
        ),
    )
    # Parcel
    p = django_filters.CharFilter(
        method="filter_parcel",
        label=_(""),
        label_suffix=_(""),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("Parcel"),
                "title": _("Search for parcel"),
                "class": "form-control",
            }
        ),
    )
    # Parcel counter
    pc = django_filters.CharFilter(
        method="filter_parcel_counter",
        label=_(""),
        label_suffix=_(""),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("Parcel counter"),
                "title": _("Search for parcel counter"),
                "class": "form-control",
            }
        ),
    )

    # Parcel counter
    pn = django_filters.CharFilter(
        method="filter_parcel_number",
        label=_(""),
        label_suffix=_(""),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("Parcel number"),
                "title": _("Search for parcel number"),
                "class": "form-control",
            }
        ),
    )

    class Meta:
        abstract = True

    def _filter_parcel_reference(self, queryset, filter_q) -> QuerySet:
        """ Filters the parcel entries by a given filter_q

        Args:
            queryset (QuerySet): The queryset
            filter_q (Q): The Q-style filter expression

        Returns:

        """
        matching_parcels = Parcel.objects.filter(
            filter_q
        )

        related_geoms = matching_parcels.values(
            "geometries"
        ).distinct()
        queryset = queryset.filter(
            geometry__id__in=related_geoms
        )
        return queryset

    def filter_district(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value for 'Gemarkung'

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        matching_districts = District.objects.filter(
            Q(name__icontains=value) |
            Q(key__icontains=value)
        ).distinct()
        matching_parcels = Parcel.objects.filter(
            district__in=matching_districts
        )
        related_geoms = matching_parcels.values(
            "geometries"
        ).distinct()
        queryset = queryset.filter(
            geometry__id__in=related_geoms
        )
        return queryset

    def filter_gmrkng(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value for 'Gemarkung'

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        queryset = self._filter_parcel_reference(
            queryset,
            Q(parcel_group__name__icontains=value) | Q(parcel_group__key__icontains=value),
        )
        return queryset

    def filter_parcel(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value for 'Parcel'

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        value = value.replace("-", "")
        queryset = self._filter_parcel_reference(
            queryset,
            Q(flr=value),
        )
        return queryset

    def filter_parcel_counter(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value for 'Parcel'

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        value = value.replace("-", "")
        queryset = self._filter_parcel_reference(
            queryset,
            Q(flrstck_zhlr=value)
        )
        return queryset

    def filter_parcel_number(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value for 'Parcel'

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        value = value.replace("-", "")
        queryset = self._filter_parcel_reference(
            queryset,
            Q(flrstck_nnr=value),
        )
        return queryset


class ShareableTableFilterMixin(django_filters.FilterSet):
    """ A mixin for AbstractTableFilter

    Specialized on filtering shareable model types

    """
    sa = django_filters.BooleanFilter(
        method='filter_show_all',
        label=_("Show unshared"),
        label_suffix=_(""),
        widget=forms.CheckboxInput(
            attrs={
                "class": "form-check-input",
            }
        )
    )

    class Meta:
        abstract = True

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop("user", None)
        if self.user is None:
            raise AttributeError("User must be set for further filtering!")
        super().__init__(*args, **kwargs)

    def filter_show_all(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value of 'show_all' setting

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        if not value:
            return queryset.filter(
                Q(users__in=[self.user]) |  # requesting user has access
                Q(teams__in=self.user.shared_teams)
            ).distinct()
        else:
            return queryset


class RecordableTableFilterMixin(django_filters.FilterSet):
    """ A mixin for AbstractTableFilter

    Specialized on filtering recordable model types

    """
    sr = django_filters.BooleanFilter(
        method='filter_show_recorded',
        label=_("Show recorded"),
        label_suffix=_(""),
        widget=forms.CheckboxInput(
            attrs={
                "class": "form-check-input",
            }
        )
    )

    class Meta:
        abstract = True

    def filter_show_recorded(self, queryset, name, value) -> QuerySet:
        """ Filters queryset depending on value of 'show_recorded' setting

        Args:
            queryset ():
            name ():
            value ():

        Returns:

        """
        if not value:
            return queryset.filter(
                recorded=None,
            )
        else:
            return queryset


class RegistrationOfficeTableFilterMixin(django_filters.FilterSet):
    """ A mixin for AbstractTableFilter

    Specialized on filtering for related registration offices

    """
    ro = django_filters.ModelChoiceFilter(
        method="filter_reg_office",
        label=_(""),
        label_suffix=_(""),
        queryset=KonovaCode.objects.filter(
            is_archived=False,
            is_leaf=True,
            code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
        ),
        widget=ModelSelect2(
            url="codes-registration-office-autocomplete",
            attrs={
                "data-placeholder": _("Registration office"),
                "title": _("Search for registration office"),
                "class": "",
            }
        ),
    )

    def filter_reg_office(self, queryset, name, value):
        qs = queryset.filter(
            responsible__registration_office=value
        )
        return qs


class ConservationOfficeTableFilterMixin(django_filters.FilterSet):
    """ A mixin for AbstractTableFilter

    Specialized on filtering for related conservation offices

    """
    co = django_filters.ModelChoiceFilter(
        method="filter_cons_office",
        label=_(""),
        label_suffix=_(""),
        queryset=KonovaCode.objects.filter(
            is_archived=False,
            is_leaf=True,
            code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
        ),
        widget=ModelSelect2(
            url="codes-conservation-office-autocomplete",
            attrs={
                "data-placeholder": _("Conservation office"),
                "title": _("Search for conservation office"),
                "class": "",
            }
        ),
    )

    def filter_cons_office(self, queryset, name, value):
        qs = queryset.filter(
            responsible__conservation_office=value
        )
        return qs
