# coding: utf-8

import re
from collections import OrderedDict

from django.db.models import QuerySet
from django.utils.translation import gettext as _
from rest_framework import exceptions, pagination
from rest_framework.response import Response


class LimitOffsetPagination(pagination.LimitOffsetPagination):
    template = None

    def paginate_queryset(self, queryset, request, view=None):
        self.limit = self.get_limit(request)
        if self.limit is None:
            return None

        self.offset = self.get_offset(request)
        self.request = request

        return list(queryset[self.offset : self.offset + self.limit])

    def get_paginated_response(self, data):
        return Response(
            OrderedDict(
                [
                    ('limit', self.limit),
                    ('offset', self.offset),
                    ('results', data),
                ]
            )
        )


class PageNumberPagination(pagination.PageNumberPagination):
    page_size_query_param = 'limit'

    def get_paginated_response(self, data):
        return Response(
            OrderedDict(
                [
                    ('count', self.page.paginator.count),
                    ('page', self.page.number),
                    ('limit', self.page.paginator.per_page),
                    ('num_pages', self.page.paginator.num_pages),
                    ('results', data),
                ]
            )
        )


class NoPagination(pagination.BasePagination):
    def paginate_queryset(self, queryset, request, view=None):
        return queryset

    def get_paginated_response(self, data):
        return Response(OrderedDict([('results', data)]))


class NextPagination(pagination.PageNumberPagination):
    page_size_query_param = 'limit'
    page = has_next = limit = None

    def paginate_queryset(self, queryset, request, view=None):
        assert isinstance(queryset, QuerySet)

        limit = self.limit = self.get_page_size(request)
        if not limit:
            return None

        try:
            page = self.page = pagination._positive_int(
                request.query_params[self.page_query_param], strict=True
            )
        except (ValueError, KeyError):
            page = self.page = 1

        def get_slice(page):
            bottom = (page - 1) * limit
            top = bottom + limit
            return slice(bottom, top)

        nqs = queryset

        # FIXME
        # Django 2.0 fails to execute the query with no distinct fields
        nqs = nqs.distinct(
            *[re.sub(r'-?(.+)', r'\1', field) for field in nqs.query.order_by]
        )

        if nqs.query.select_related:
            nqs = nqs.select_related(None)

        if not nqs._fields:
            nqs = nqs.only('id')

        has_next = bool(nqs[get_slice(page + 1)].count())
        self.num_pages = page + has_next

        result = list(queryset[get_slice(page)])

        if not result and page > 1:
            raise exceptions.NotFound(_('Invalid page.'))

        return result

    def get_paginated_response(self, data):
        return Response(
            OrderedDict(
                [
                    ('page', self.page),
                    ('limit', self.limit),
                    ('num_pages', self.num_pages),
                    ('results', data),
                ]
            )
        )
