""".. Ignore pydocstyle D400.

============
Flow Filters
============

"""
import django_filters as filters

from rest_framework.exceptions import ParseError

from .models import Collection, Data, DescriptorSchema, Entity, Process, Relation

NUMBER_LOOKUPS = [
    "exact",
    "in",
    "gt",
    "gte",
    "lt",
    "lte",
    "isnull",
]
TEXT_LOOKUPS = [
    "exact",
    "iexact",
    "contains",
    "icontains",
    "in",
    "startswith",
    "istartswith",
    "endswith",
    "iendswith",
    "isnull",
]
DATE_LOOKUPS = [
    "exact",
    "gt",
    "gte",
    "lt",
    "lte",
    "year",
    "year__gt",
    "year__gte",
    "year__lt",
    "year__lte",
    "month",
    "month__gt",
    "month__gte",
    "month__lt",
    "month__lte",
    "day",
    "day__gt",
    "day__gte",
    "day__lt",
    "day__lte",
    "isnull",
]
DATETIME_LOOKUPS = DATE_LOOKUPS + [
    "date",
    "time",
    "hour",
    "hour__gt",
    "hour__gte",
    "hour__lt",
    "hour__lte",
    "minute",
    "minute__gt",
    "minute__gte",
    "minute__lt",
    "minute__lte",
    "second",
    "second__gt",
    "second__gte",
    "second__lt",
    "second__lte",
]


class CheckQueryParamsMixin:
    """Custom query params validation."""

    def get_always_allowed_arguments(self):
        """Get always allowed query arguments."""
        return (
            "fields",
            "format",
            "limit",
            "offset",
            "ordering",
        )

    def validate_query_params(self):
        """Ensure no unsupported query params were used."""
        allowed_params = set(self.get_filters().keys())
        allowed_params.update(self.get_always_allowed_arguments())

        unallowed = set(self.request.query_params.keys()) - allowed_params

        if unallowed:
            msg = "Unsupported parameter(s): {}. Please use a combination of: {}.".format(
                ", ".join(unallowed), ", ".join(allowed_params),
            )
            self.form.add_error(field=None, error=ParseError(msg))

    def is_valid(self):
        """Validate filterset."""
        self.validate_query_params()
        return super().is_valid()


class BaseResolweFilter(CheckQueryParamsMixin, filters.FilterSet):
    """Base filter for Resolwe's endpoints."""

    class Meta:
        """Filter configuration."""

        fields = {
            "id": NUMBER_LOOKUPS[:],
            "slug": TEXT_LOOKUPS[:],
            "name": TEXT_LOOKUPS[:],
            "contributor": ["exact", "in"],
            "created": DATETIME_LOOKUPS[:],
            "modified": DATETIME_LOOKUPS[:],
        }


class DescriptorSchemaFilter(BaseResolweFilter):
    """Filter the DescriptorSchema endpoint."""

    class Meta(BaseResolweFilter.Meta):
        """Filter configuration."""

        model = DescriptorSchema


class CollectionFilter(BaseResolweFilter):
    """Filter the Collection endpoint."""

    data = filters.ModelChoiceFilter(queryset=Data.objects.all())
    entity = filters.ModelChoiceFilter(queryset=Entity.objects.all())

    class Meta(BaseResolweFilter.Meta):
        """Filter configuration."""

        model = Collection
        fields = {
            **BaseResolweFilter.Meta.fields,
            **{"description": TEXT_LOOKUPS[:], "descriptor_schema": ["exact"],},
        }


class TagsFilter(filters.filters.BaseCSVFilter, filters.CharFilter):
    """Filter for tags."""

    def __init__(self, *args, **kwargs):
        """Construct tags filter."""
        kwargs.setdefault("lookup_expr", "contains")
        super().__init__(*args, **kwargs)


class EntityFilter(CollectionFilter):
    """Filter the Entity endpoint."""

    collection = filters.ModelChoiceFilter(
        field_name="collection", queryset=Collection.objects.all()
    )
    tags = TagsFilter()

    class Meta(CollectionFilter.Meta):
        """Filter configuration."""

        model = Entity


class ProcessFilter(BaseResolweFilter):
    """Filter the Process endpoint."""

    category = filters.CharFilter(field_name="category", lookup_expr="startswith")
    type = filters.CharFilter(field_name="type", lookup_expr="startswith")
    is_active = filters.rest_framework.filters.BooleanFilter(field_name="is_active")

    class Meta(BaseResolweFilter.Meta):
        """Filter configuration."""

        model = Process
        fields = {**BaseResolweFilter.Meta.fields, **{"scheduling_class": ["exact"],}}


class CharInFilter(filters.BaseInFilter, filters.CharFilter):
    """Helper class for creation of CharFilter with "in" lookup."""


class DataFilter(BaseResolweFilter):
    """Filter the Data endpoint."""

    collection = filters.ModelChoiceFilter(queryset=Collection.objects.all())
    collection__slug = filters.CharFilter(
        field_name="collection__slug", lookup_expr="exact"
    )

    entity = filters.ModelChoiceFilter(queryset=Entity.objects.all())

    type = filters.CharFilter(field_name="process__type", lookup_expr="startswith")
    status = filters.CharFilter(lookup_expr="iexact")
    status__in = CharInFilter(field_name="status", lookup_expr="in")

    tags = TagsFilter()

    class Meta(BaseResolweFilter.Meta):
        """Filter configuration."""

        model = Data
        fields = {
            **BaseResolweFilter.Meta.fields,
            **{
                "process": ["exact"],
                "process__slug": ["exact"],
                "finished": DATETIME_LOOKUPS[:],
                "started": DATETIME_LOOKUPS[:],
            },
        }


class RelationFilter(BaseResolweFilter):
    """Filter the Relation endpoint."""

    category = filters.CharFilter(lookup_expr="iexact")
    collection = filters.ModelChoiceFilter(queryset=Collection.objects.all())
    type = filters.CharFilter(field_name="type__name")

    class Meta(BaseResolweFilter.Meta):
        """Filter configuration."""

        model = Relation
        fields = BaseResolweFilter.Meta.fields

    def get_always_allowed_arguments(self):
        """Get always allowed query arguments."""
        return super().get_always_allowed_arguments() + ("entity", "label", "position",)
