"""API filters for Impress' core application."""

import unicodedata

from django.db.models import CharField, Func
from django.utils.translation import gettext_lazy as _

import django_filters

from core import models


def remove_accents(value):
    """Remove accents from a string (vélo -> velo)."""
    return "".join(
        c
        for c in unicodedata.normalize("NFD", value)
        if unicodedata.category(c) != "Mn"
    )


# pylint: disable=abstract-method
class Unaccent(Func):
    """
    PostgreSQL unaccent function wrapper for use in Django ORM queries.

    This allows you to annotate a field using the unaccented version of a
    text column, enabling accent-insensitive filtering.
    """

    function = "unaccent"
    template = "unaccent(%(expressions)s::text)"
    output_field = CharField()


class AccentInsensitiveCharFilter(django_filters.CharFilter):
    """
    A custom CharFilter that performs case-insensitive and accent-insensitive filtering.

    This filter uses PostgreSQL's extension `unaccent` function to remove diacritics (accents)
    from characters before applying the lookup expression (e.g., `icontains`).
    """

    def filter(self, qs, value):
        """
        Apply the filter to the queryset using the unaccented version of the field.

        Args:
            qs: The queryset to filter.
            value: The value to search for in the unaccented field.

        Returns:
            A filtered queryset.
        """
        if value:
            value = remove_accents(value)
            field_name = self.field_name
            annotated_field = f"unaccented_{field_name}"
            return qs.annotate(**{annotated_field: Unaccent(field_name)}).filter(
                **{f"{annotated_field}__{self.lookup_expr}": value}
            )
        return qs


class DocumentFilter(django_filters.FilterSet):
    """
    Custom filter for filtering documents on title (accent and case insensitive).
    """

    title = AccentInsensitiveCharFilter(
        field_name="title", lookup_expr="icontains", label=_("Title")
    )

    class Meta:
        model = models.Document
        fields = ["title"]


class ListDocumentFilter(DocumentFilter):
    """
    Custom filter for filtering documents.
    """

    is_creator_me = django_filters.BooleanFilter(
        method="filter_is_creator_me", label=_("Creator is me")
    )
    is_favorite = django_filters.BooleanFilter(
        method="filter_is_favorite", label=_("Favorite")
    )

    class Meta:
        model = models.Document
        fields = ["is_creator_me", "is_favorite", "title"]

    # pylint: disable=unused-argument
    def filter_is_creator_me(self, queryset, name, value):
        """
        Filter documents based on the `creator` being the current user.

        Example:
            - /api/v1.0/documents/?is_creator_me=true
                → Filters documents created by the logged-in user
            - /api/v1.0/documents/?is_creator_me=false
                → Filters documents created by other users
        """
        user = self.request.user

        if not user.is_authenticated:
            return queryset

        if value:
            return queryset.filter(creator=user)

        return queryset.exclude(creator=user)

    # pylint: disable=unused-argument
    def filter_is_favorite(self, queryset, name, value):
        """
        Filter documents based on whether they are marked as favorite by the current user.

        Example:
            - /api/v1.0/documents/?is_favorite=true
                → Filters documents marked as favorite by the logged-in user
            - /api/v1.0/documents/?is_favorite=false
                → Filters documents not marked as favorite by the logged-in user
        """
        user = self.request.user

        if not user.is_authenticated:
            return queryset

        return queryset.filter(is_favorite=bool(value))
