from mock import patch, sentinel, Mock, PropertyMock, DEFAULT
from unittest import TestCase

from distribution.utils.paginator import get_url_for_page, Paginator


class PaginatorHelpersTestCase(TestCase):
    @patch('distribution.utils.paginator.request')
    @patch('distribution.utils.paginator.url_for', return_value=sentinel.URL)
    def test_get_url_for_page(self, url_for_mock, request_mock):
        def reset_mocks():
            request_mock.reset_mock()
            url_for_mock.reset_mock()

        request_mock.endpoint = sentinel.ENDPOINT
        request_mock.view_args = dict(view_arg1=sentinel.VIEW_ARG1,
                                      view_arg2=sentinel.VIEW_ARG2)
        request_mock.args.to_dict.return_value = dict(arg1=sentinel.ARG1,
                                                      arg2=sentinel.ARG2,
                                                      apikey=sentinel.APIKEY,
                                                      apiuser=sentinel.APIUSER)

        # defaults
        result = get_url_for_page(sentinel.PAGE)

        url_for_mock.assert_called_once_with(sentinel.ENDPOINT, **{
            "arg1": sentinel.ARG1, "arg2": sentinel.ARG2,
            "view_arg1": sentinel.VIEW_ARG1, "view_arg2": sentinel.VIEW_ARG2,
            "page": sentinel.PAGE, "_external": True})
        self.assertEqual(result, sentinel.URL)
        reset_mocks()

        # override endpoint
        get_url_for_page(sentinel.PAGE, endpoint=sentinel.ENDPOINT_ALT)
        url_for_mock.assert_called_once_with(sentinel.ENDPOINT_ALT, **{
            "arg1": sentinel.ARG1, "arg2": sentinel.ARG2,
            "view_arg1": sentinel.VIEW_ARG1, "view_arg2": sentinel.VIEW_ARG2,
            "page": sentinel.PAGE, "_external": True})
        reset_mocks()

        # override view_args
        get_url_for_page(sentinel.PAGE, view_args={
            "view_args_alt": sentinel.VIEW_ARGS_ALT})
        url_for_mock.assert_called_once_with(sentinel.ENDPOINT, **{
            "arg1": sentinel.ARG1, "arg2": sentinel.ARG2,
            "view_args_alt": sentinel.VIEW_ARGS_ALT, "page": sentinel.PAGE,
            "_external": True})
        reset_mocks()

        # override args
        get_url_for_page(sentinel.PAGE, args={"args_alt": sentinel.ARGS_ALT})
        url_for_mock.assert_called_once_with(sentinel.ENDPOINT, **{
            "args_alt": sentinel.ARGS_ALT, "view_arg1": sentinel.VIEW_ARG1,
            "view_arg2": sentinel.VIEW_ARG2, "page": sentinel.PAGE,
            "_external": True})
        reset_mocks()

        # override exclude_args
        get_url_for_page(sentinel.PAGE, exclude_args=['arg2'])
        url_for_mock.assert_called_once_with(sentinel.ENDPOINT, **{
            "arg1": sentinel.ARG1, "apikey": sentinel.APIKEY,
            "apiuser": sentinel.APIUSER, "view_arg1": sentinel.VIEW_ARG1,
            "view_arg2": sentinel.VIEW_ARG2, "page": sentinel.PAGE,
            "_external": True})
        self.assertEqual(result, sentinel.URL)
        reset_mocks()

        # override full_url
        get_url_for_page(sentinel.PAGE, full_url=False)
        url_for_mock.assert_called_once_with(sentinel.ENDPOINT, **{
            "arg1": sentinel.ARG1, "arg2": sentinel.ARG2,
            "view_arg1": sentinel.VIEW_ARG1, "view_arg2": sentinel.VIEW_ARG2,
            "page": sentinel.PAGE, "_external": False})
        self.assertEqual(result, sentinel.URL)


class PaginatorTestCase(TestCase):
    def setUp(self):

        get_url_for_page_m = Mock(get_url_for_page, return_value=sentinel.URL)
        self.url_for_page_prop = PropertyMock(return_value=get_url_for_page_m)

        self.paginator = Paginator(sentinel.PAGE, sentinel.PER_PAGE,
                                   sentinel.TOTAL_COUNT)

    def test_init(self):
        pag = Paginator(sentinel.PAGE, sentinel.PER_PAGE, sentinel.TOTAL_COUNT)

        self.assertEqual(pag.page, sentinel.PAGE)
        self.assertEqual(pag.per_page, sentinel.PER_PAGE)
        self.assertEqual(pag.total_count, sentinel.TOTAL_COUNT)
        self.assertEqual(pag.last_reachable_result, sentinel.TOTAL_COUNT)

        pag = Paginator(sentinel.PAGE, sentinel.PER_PAGE, total_count=100,
                        max_result_window=25)
        self.assertEqual(pag.last_reachable_result, 25,
                         "last_reachable_result should not exceed the give "
                         "max_result_window limit.")

        pag = Paginator(sentinel.PAGE, sentinel.PER_PAGE, total_count=25,
                        max_result_window=100)
        self.assertEqual(pag.last_reachable_result, 25,
                         "last_reachable_result should not go beyond the "
                         "actual number of results (total_count).")

    def test_pages(self):
        self.assertEqual(Paginator(1, per_page=25, total_count=0).pages, 0)
        self.assertEqual(Paginator(1, per_page=25, total_count=25).pages, 1)
        self.assertEqual(Paginator(1, per_page=25, total_count=26).pages, 2)
        self.assertEqual(Paginator(1, per_page=1, total_count=25).pages, 25)
        self.assertEqual(Paginator(1, per_page=25, total_count=666,
                                   max_result_window=10).pages, 1)

    def test_has_prev(self):
        self.assertFalse(Paginator(1, per_page=25, total_count=100).has_prev)
        self.assertTrue(Paginator(2, per_page=25, total_count=100).has_prev)
        self.assertTrue(Paginator(666, per_page=25, total_count=100).has_prev)

    def test_has_next(self):
        self.assertTrue(Paginator(1, per_page=25, total_count=100).has_next)
        self.assertFalse(Paginator(4, per_page=25, total_count=100).has_next)
        self.assertFalse(Paginator(666, per_page=25, total_count=100).has_next)

    def test_page_url(self):
        with patch('distribution.utils.paginator.Paginator.url_for_page',
                   new_callable=self.url_for_page_prop) as url_for_page_mock:

            self.paginator.page = 0
            self.assertEqual(self.paginator.page_url, sentinel.URL)

            self.paginator.page = 1
            self.assertEqual(self.paginator.page_url, sentinel.URL)

            self.paginator.page = 2
            self.assertEqual(self.paginator.page_url, sentinel.URL)

            self.paginator.page = 666
            self.assertEqual(self.paginator.page_url, sentinel.URL)

            self.assertEqual(url_for_page_mock.call_args_list,
                             [((0,),), ((None,),), ((2,),), ((666,),)])

    def test_first_page_url(self):
        with patch('distribution.utils.paginator.Paginator.url_for_page',
                   new_callable=self.url_for_page_prop) as url_for_page_mock:

            self.paginator.page = 0
            self.assertEqual(self.paginator.first_page_url, sentinel.URL)

            self.paginator.page = 1
            self.assertEqual(self.paginator.first_page_url, sentinel.URL)

            self.paginator.page = 666
            self.assertEqual(self.paginator.first_page_url, sentinel.URL)

            self.assertEqual(url_for_page_mock.call_args_list,
                             [((1,),), ((1,),), ((1,),)])

    def test_previous_page_url(self):
        with patch('distribution.utils.paginator.Paginator.url_for_page',
                   new_callable=self.url_for_page_prop) as url_for_page_mock:

            self.paginator.page = 0
            self.assertEqual(self.paginator.previous_page_url, sentinel.URL)

            self.paginator.page = 1
            self.assertEqual(self.paginator.previous_page_url, sentinel.URL)

            self.paginator.page = 666
            self.assertEqual(self.paginator.previous_page_url, sentinel.URL)

            self.assertEqual(url_for_page_mock.call_args_list,
                             [((-1,),), ((0,),), ((665,),)])

    def test_next_page_url(self):
        with patch('distribution.utils.paginator.Paginator.url_for_page',
                   new_callable=self.url_for_page_prop) as url_for_page_mock:

            self.paginator.page = 0
            self.assertEqual(self.paginator.next_page_url, sentinel.URL)

            self.paginator.page = 1
            self.assertEqual(self.paginator.next_page_url, sentinel.URL)

            self.paginator.page = 666
            self.assertEqual(self.paginator.next_page_url, sentinel.URL)

            self.assertEqual(url_for_page_mock.call_args_list,
                             [((1,),), ((2,),), ((667,),)])

    def test_last_page_url(self):
        with patch('distribution.utils.paginator.Paginator.url_for_page',
                   new_callable=self.url_for_page_prop) as url_for_page_mock:

            with patch('distribution.utils.paginator.Paginator.pages',
                       new_callable=PropertyMock) as pages_mock:

                pages_mock.return_value = 0
                self.assertEqual(self.paginator.last_page_url, sentinel.URL)

                pages_mock.return_value = 1
                self.assertEqual(self.paginator.last_page_url, sentinel.URL)

                pages_mock.return_value = 666
                self.assertEqual(self.paginator.last_page_url, sentinel.URL)

                self.assertEqual(url_for_page_mock.call_args_list,
                                 [((0,),), ((1,),), ((666,),)])

    def test_links(self):
        page_url_mock = PropertyMock(return_value=sentinel.URL)
        first_page_url_mock = PropertyMock(return_value=sentinel.FIRST_URL)
        last_page_url = PropertyMock(return_value=sentinel.LAST_URL)
        previous_page_url = PropertyMock(return_value=sentinel.PREVIOUS_URL)
        next_page_url = PropertyMock(return_value=sentinel.NEXT_URL)

        with patch.multiple(Paginator, page_url=page_url_mock,
                            first_page_url=first_page_url_mock,
                            last_page_url=last_page_url,
                            previous_page_url=previous_page_url,
                            next_page_url=next_page_url):

            # No results:
            self.assertEqual(Paginator(1, 20, 0).links, {"self": sentinel.URL})

            # First and only:
            self.assertEqual(Paginator(1, 20, 20).links, {
                "self": sentinel.URL,
                "first": sentinel.FIRST_URL,
                "last": sentinel.LAST_URL
            })

            # First of many:
            self.assertEqual(Paginator(1, 20, 200).links, {
                "self": sentinel.URL,
                "first": sentinel.FIRST_URL,
                "last": sentinel.LAST_URL,
                "next": sentinel.NEXT_URL
            })

            # Last of many:
            self.assertEqual(Paginator(10, 20, 200).links, {
                "self": sentinel.URL,
                "first": sentinel.FIRST_URL,
                "last": sentinel.LAST_URL,
                "prev": sentinel.PREVIOUS_URL
            })

            # Second of many:
            self.assertEqual(Paginator(2, 20, 200).links, {
                "self": sentinel.URL,
                "first": sentinel.FIRST_URL,
                "last": sentinel.LAST_URL,
                "prev": sentinel.PREVIOUS_URL,
                "next": sentinel.NEXT_URL
            })
