# -*- coding: utf-8 -*-

from __future__ import unicode_literals

from django.core.cache import cache
from django.test import TestCase, RequestFactory
from django.template import Template, Context
from django.test.utils import override_settings
from django.http import Http404
from django.core.paginator import Page, Paginator

from . import utils

from spirit.models.comment import Comment
from spirit.utils.paginator.yt_paginator import YTPaginator, InvalidPage, YTPage
from spirit.utils import paginator
from spirit.utils.paginator import infinite_paginator, paginate, yt_paginate
from spirit.templatetags.tags.utils.paginator import render_paginator
from spirit.templatetags.tags.utils import paginator as ttag_paginator


class UtilsPaginatorTest(TestCase):

    def setUp(self):
        cache.clear()

    def test_paginator_page(self):
        per_page = 15
        obj_number = 1
        self.assertEqual(paginator.get_page_number(obj_number=obj_number, per_page=per_page), 1)
        obj_number = per_page
        self.assertEqual(paginator.get_page_number(obj_number=obj_number, per_page=per_page), 1)
        obj_number = per_page - 1
        self.assertEqual(paginator.get_page_number(obj_number=obj_number, per_page=per_page), 1)
        obj_number = per_page + 1
        self.assertEqual(paginator.get_page_number(obj_number=obj_number, per_page=per_page), 2)

    def test_paginator_url(self):
        per_page = 15
        obj_number = 1
        page_var = "page"
        url = "/path/"
        first_page = url + '#c' + str(obj_number)
        self.assertEqual(paginator.get_url(url, obj_number, per_page, page_var), first_page)
        obj_number = 16
        expected = '%(url)s?%(page_var)s=%(page_num)s#c%(obj_number)s' % {'url': url,
                                                                          'page_var': page_var,
                                                                          'page_num': 2,
                                                                          'obj_number': obj_number}
        self.assertEqual(paginator.get_url(url, obj_number, per_page, page_var), expected)


class UtilsInfinitePaginatorTest(TestCase):

    def setUp(self):
        cache.clear()
        self.user = utils.create_user()
        self.topic = utils.create_topic(utils.create_category())

        for _ in range(300):
            utils.create_comment(user=self.user, topic=self.topic)

        self.queryset = Comment.objects.all().order_by("-pk")

    def test_paginate(self):
        # first page
        req = RequestFactory().get('/')
        page = infinite_paginator.paginate(req, self.queryset, per_page=15, lookup_field="pk")
        page_last_pk = list(self.queryset[:15])[-1].pk
        self.assertEqual(page.next_page_pk(), page_last_pk)

        # second page
        page_last_pk = list(self.queryset[:15])[-1].pk
        req = RequestFactory().get('/?id=%s' % page_last_pk)
        page = infinite_paginator.paginate(req, self.queryset, per_page=15, lookup_field="pk", page_var='id')
        second_page_last_pk = list(self.queryset[15:30])[-1].pk
        self.assertEqual(page.next_page_pk(), second_page_last_pk)

        # invalid (id) page
        last_pk = self.queryset.order_by("pk").last().pk
        req = RequestFactory().get('/?id=%s' % (last_pk + 1))
        self.assertRaises(Http404, infinite_paginator.paginate,
                          req, self.queryset, per_page=15, lookup_field="pk", page_var='id')

        # empty page
        valid_pk = self.queryset.last().pk
        req = RequestFactory().get('/?id=%s' % valid_pk)
        self.assertRaises(Http404, infinite_paginator.paginate,
                          req, self.queryset.none(), per_page=15, lookup_field="pk", page_var='id')

        # empty first page
        req = RequestFactory().get('/')
        page = infinite_paginator.paginate(req, self.queryset.none(), per_page=15, lookup_field="pk")
        self.assertEqual(len(page), 0)


class UtilsYTPaginatorTests(TestCase):

    def setUp(self):
        cache.clear()
        self.user = utils.create_user()
        self.topic = utils.create_topic(utils.create_category())

        for _ in range(300):
            utils.create_comment(user=self.user, topic=self.topic)

        self.queryset = Comment.objects.all()

    def test_yt_paginator_page(self):
        yt_paginator = YTPaginator(self.queryset, per_page=10)
        page = yt_paginator.page(1)
        self.assertEqual(page.number, 1)
        self.assertListEqual([i for i in page], list(self.queryset[:10]))

        # empty first page
        yt_paginator = YTPaginator(self.queryset.none(), per_page=10)
        self.assertListEqual([i for i in yt_paginator.page(1)], [])

    def test_yt_paginator_page_invalid(self):
        yt_paginator = YTPaginator(self.queryset, per_page=10)
        self.assertRaises(InvalidPage, lambda: yt_paginator.page("one"))
        self.assertRaises(InvalidPage, lambda: yt_paginator.page(-1))
        self.assertRaises(InvalidPage, lambda: yt_paginator.page(5000))

    def tests_yt_paginator_num_pages(self):
        yt_paginator = YTPaginator(self.queryset, per_page=10)
        page = yt_paginator.page(1)

        for page_num in range(1, 30 + 1):
            page._num_pages = None
            page._max_pages = page_num
            self.assertEqual(page.num_pages, page_num)

        page._num_pages = None
        page._max_pages = 8
        self.assertEqual(page.num_pages, 8)

        page._num_pages = None
        page._max_pages = 3000
        self.assertEqual(page.num_pages, 30)

    @override_settings(ST_YT_PAGINATOR_PAGE_RANGE=3)
    def test_yt_paginator_page_range(self):
        # 10 pages
        yt_paginator = YTPaginator(list(range(0, 100)), per_page=10)

        page = yt_paginator.page(1)
        self.assertListEqual(list(page.page_range), [1, 2, 3, 4, 5, 6, 7])

        page = yt_paginator.page(4)
        self.assertListEqual(list(page.page_range), [1, 2, 3, 4, 5, 6, 7])

        page = yt_paginator.page(7)
        self.assertListEqual(list(page.page_range), [4, 5, 6, 7, 8, 9, 10])

        page = yt_paginator.page(10)
        self.assertListEqual(list(page.page_range), [4, 5, 6, 7, 8, 9, 10])

        # 2 pages
        yt_paginator = YTPaginator(list(range(0, 20)), per_page=10)

        page = yt_paginator.page(1)
        self.assertListEqual(list(page.page_range), [1, 2])

        page = yt_paginator.page(2)
        self.assertListEqual(list(page.page_range), [1, 2])


class UtilsYTPaginatorTemplateTagsTests(TestCase):

    def setUp(self):
        cache.clear()

    def tests_yt_paginate(self):
        # first page
        items = list(range(0, 20))
        page = yt_paginate(items, per_page=10)
        self.assertIsInstance(page, YTPage)
        self.assertEqual(list(page), items[:10])

        # second page
        page = yt_paginate(items, per_page=10, page_number=2)
        self.assertEqual(list(page), items[10:20])

        # invalid page
        self.assertRaises(Http404, yt_paginate,
                          items, per_page=10, page_number=99)

        # empty first page
        page = yt_paginate([], per_page=10)
        self.assertListEqual(list(page), [])

    def tests_render_yt_paginator(self):
        def mock_render(template, context):
            return template, context

        req = RequestFactory().get('/')
        context = {'request': req, }
        items = list(range(0, 20))
        page = YTPaginator(items, per_page=10).page(1)

        org_render, ttag_paginator.render_to_string = ttag_paginator.render_to_string, mock_render
        try:
            template, context2 = render_paginator(context, page)
            self.assertDictEqual(context2, {"page": page,
                                            "page_var": 'page',
                                            "hashtag": '',
                                            "extra_query": ''})
            self.assertEqual(template, "spirit/paginator/_yt_paginator.html")
        finally:
            ttag_paginator.render_to_string = org_render

    def tests_render_yt_paginator_extra(self):
        def mock_render(template, context):
            return template, context

        req = RequestFactory().get('/?foo_page=1&extra=foo')
        context = {'request': req, }
        items = list(range(0, 20))
        page = YTPaginator(items, per_page=10).page(1)

        org_render, ttag_paginator.render_to_string = ttag_paginator.render_to_string, mock_render
        try:
            template, context2 = render_paginator(context, page, page_var='foo_page', hashtag="c20")
            self.assertDictEqual(context2, {"page": page,
                                            "page_var": 'foo_page',
                                            "hashtag": '#c20',
                                            "extra_query": '&extra=foo'})
            self.assertEqual(template, "spirit/paginator/_yt_paginator.html")
        finally:
            ttag_paginator.render_to_string = org_render


class UtilsPaginatorTemplateTagsTests(TestCase):

    def setUp(self):
        cache.clear()

    def tests_paginate(self):
        # first page
        items = list(range(0, 20))
        page = paginate(items, per_page=10)
        self.assertIsInstance(page, Page)
        self.assertEqual(list(page), items[:10])

        # second page
        page = paginate(items, per_page=10, page_number=2)
        self.assertEqual(list(page), items[10:20])

        # invalid page
        self.assertRaises(Http404, paginate,
                          items, per_page=10, page_number=99)

        # empty first page
        page = paginate([], per_page=10)
        self.assertListEqual(list(page), [])

    def tests_render_paginator_tag(self):
        """
        Minimal test to check it works
        """
        req = RequestFactory().get('/')
        items = list(range(0, 20))
        page = Paginator(items, per_page=10).page(1)
        Template(
            "{% load spirit_tags %}"
            "{% render_paginator page %}"
        ).render(Context({'request': req, 'page': page, }))

    def tests_render_paginator(self):
        def mock_render(template, context):
            return template, context

        req = RequestFactory().get('/')
        context = {'request': req, }
        items = list(range(0, 20))
        page = Paginator(items, per_page=10).page(1)

        org_render, ttag_paginator.render_to_string = ttag_paginator.render_to_string, mock_render
        try:
            template, context2 = render_paginator(context, page)
            self.assertDictEqual(context2, {"page": page,
                                            "page_var": 'page',
                                            "hashtag": '',
                                            "extra_query": ''})
            self.assertEqual(template, "spirit/paginator/_paginator.html")
        finally:
            ttag_paginator.render_to_string = org_render

    def tests_render_paginator_extra(self):
        def mock_render(template, context):
            return template, context

        req = RequestFactory().get('/?foo_page=1&extra=foo')
        context = {'request': req, }
        items = list(range(0, 20))
        page = Paginator(items, per_page=10).page(1)

        org_render, ttag_paginator.render_to_string = ttag_paginator.render_to_string, mock_render
        try:
            template, context2 = render_paginator(context, page, page_var='foo_page', hashtag="c20")
            self.assertDictEqual(context2, {"page": page,
                                            "page_var": 'foo_page',
                                            "hashtag": '#c20',
                                            "extra_query": '&extra=foo'})
            self.assertEqual(template, "spirit/paginator/_paginator.html")
        finally:
            ttag_paginator.render_to_string = org_render
