# Source code for evennia.web.api.filters

    """
    FilterSets allow clients to specify querystrings that will determine the data
    that is retrieved in GET requests. By default, Django Rest Framework uses the
    'django-filter' package as its backend. Django-filter also has a section in its
    documentation specifically regarding DRF integration.

    https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html

    """
    from typing import Union
    from django.db.models import Q
    from django_filters.rest_framework.filterset import FilterSet
    from django_filters.filters import CharFilter, EMPTY_VALUES

    from evennia.objects.models import ObjectDB
    from evennia.accounts.models import AccountDB
    from evennia.scripts.models import ScriptDB

    def get_tag_query(tag_type: Union[str, None], key: str) -> Q:
        """
        Returns a Q object for searching by tag names for typeclasses
        Args:
            tag_type(str or None): The type of tag (None, 'alias', etc)
            key (str): The name of the tag

        Returns:
            A Q object that for searching by this tag type and name

        """
        return Q(db_tags__db_tagtype=tag_type) & Q(db_tags__db_key__iexact=key)

    class TagTypeFilter(CharFilter):
        """
        This class lets you create different filters for tags of a specified db_tagtype.

        """

        tag_type = None

        def filter(self, qs, value):
            # if no value is specified, we don't use the filter
            if value in EMPTY_VALUES:
                return qs
            # if they enter a value, we filter objects by having a tag of this type with the given name
            return qs.filter(get_tag_query(self.tag_type, value)).distinct()

    class AliasFilter(TagTypeFilter):
        """A filter for objects by their aliases (tags with a tagtype of 'alias'"""

        tag_type = "alias"

    class PermissionFilter(TagTypeFilter):
        """A filter for objects by their permissions (tags with a tagtype of 'permission'"""

        tag_type = "permission"

    SHARED_FIELDS = ["db_key", "db_typeclass_path", "db_tags__db_key", "db_tags__db_category"]

    class BaseTypeclassFilterSet(FilterSet):
        """
        A parent class with filters for aliases and permissions

        """

        name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_key")
        alias = AliasFilter(lookup_expr="iexact")
        permission = PermissionFilter(lookup_expr="iexact")

        @staticmethod
        def filter_name(queryset, name, value):
            """
            Filters a queryset by aliases or the key of the typeclass
            Args:
                queryset: The queryset being filtered
                name: The name of the field
                value: The value passed in from GET params

            Returns:
                The filtered queryset
            """
            query = Q(**{f"{name}__iexact": value})
            query |= get_tag_query("alias", value)
            return queryset.filter(query).distinct()

    class ObjectDBFilterSet(BaseTypeclassFilterSet):
        """
        This adds filters for ObjectDB instances - characters, rooms, exits, etc

        """

        class Meta:
            model = ObjectDB
            fields = SHARED_FIELDS + [
                "db_location__db_key",
                "db_home__db_key",
                "db_location__id",
                "db_home__id",
            ]

    class AccountDBFilterSet(BaseTypeclassFilterSet):
        """This adds filters for Account objects"""

        name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="username")

        class Meta:
            model = AccountDB
            fields = [
                fi
                for fi in (SHARED_FIELDS + ["username", "db_is_connected", "db_is_bot"])
                if fi != "db_key"
            ]

    class ScriptDBFilterSet(BaseTypeclassFilterSet):
        """This adds filters for Script objects"""

        class Meta:
            model = ScriptDB
            fields = SHARED_FIELDS + [
                "db_desc",
                "db_obj__db_key",
                "db_obj__id",
                "db_account__id",
                "db_account__username",
                "db_is_active",
                "db_persistent",
                "db_interval",
            ]

    class HelpFilterSet(FilterSet):

        """
        Filter for help entries

        """

        name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_key")
        category = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_category")
        alias = AliasFilter(lookup_expr="iexact")