import logging

from django.db.models import QuerySet
from rest_framework_datatables.filters import DatatablesFilterBackend as DTFilterBackend

logger = logging.getLogger(__name__)
TOTAL_QUERY_COUNT = 'total-query-count'


class DatatablesFilterBackend(DTFilterBackend):
    """
    Copied from rest_framework_datatables so we can disable the
    _datatables_total_count which takes painfully long on
    log summary tables.
    """

    def filter_queryset(self, request, queryset, view):
        """
        Copy and post superclass to cache the before/after count queries.

        filter the queryset

        subclasses overriding this method should make sure to do all
        necessary steps

        -  Return unfiltered queryset if accepted renderer format is
           not 'datatables' (via `check_renderer_format`)

        - store the counts before and after filtering with
          `set_count_before` and `set_count_after`

        - respect ordering (in `ordering` key of parsed datatables
          query)
        """
        try:
            if not self.check_renderer_format(request):
                return queryset
        except AttributeError as e:
            logger.error('Unable to filter queryset: %s', e)
            return queryset.none()

        # Apply django-filter filterset BEFORE calculating counts
        # This ensures that filterset filters (like profile=<uuid>) are applied
        # before recordsTotal/recordsFiltered are calculated
        if hasattr(view, 'filterset_class') and view.filterset_class:
            from django_filters.rest_framework import DjangoFilterBackend
            django_backend = DjangoFilterBackend()
            queryset = django_backend.filter_queryset(request, queryset, view)

        total_count = filtered_count = self.get_queryset_count_before(queryset)
        self.set_count_before(view, total_count)

        datatables_query = self.parse_datatables_query(request, view)

        if q := self.get_q(datatables_query):
            queryset = queryset.filter(q).distinct()
            filtered_count = self.get_queryset_count_after(queryset)

        self.set_count_after(view, filtered_count)

        if ordering := self.get_ordering(request, view, datatables_query['fields']):
            queryset = queryset.order_by(*ordering)

        return queryset

    def get_queryset_count_before(self, queryset: QuerySet) -> int:
        """
        Provide an overrideable method to return a custom count.
        This can be useful for very large tables, as calls to model.count()
        can be very expensive.
        """
        try:
            if queryset.model.DISABLE_DATATABLES_COUNT_QUERIES:
                return 999
        except AttributeError:
            pass

        return queryset.count()

    def get_queryset_count_after(self, queryset: QuerySet) -> int:
        """
        See
        :meth:`~rest_framework_datatables.django_filters.backends.DatatablesFilterBackend.get_queryset_count_before`.
        """
        try:
            if queryset.model.DISABLE_DATATABLES_COUNT_QUERIES:
                return 333
        except AttributeError:
            pass

        return queryset.count()


__all__ = [
    'DatatablesFilterBackend',
]
