import mock
from pyramid import httpexceptions

from cliquet.errors import ERRORS

from .support import BaseWebTest, unittest, authorize, FormattedErrorMixin


class ErrorViewTest(FormattedErrorMixin, BaseWebTest, unittest.TestCase):

    sample_url = "/mushrooms"

    def test_backoff_headers_is_not_present_by_default(self):
        response = self.app.get(self.sample_url,
                                headers=self.headers, status=200)
        self.assertNotIn('Backoff', response.headers)

    def test_backoff_headers_is_present_if_configured(self):
        with mock.patch.dict(self.app.app.registry.settings,
                             [('cliquet.backoff', 10)]):
            response = self.app.get(self.sample_url,
                                    headers=self.headers, status=200)
        self.assertIn('Backoff', response.headers)

    def test_backoff_headers_is_present_on_304(self):
        first = self.app.get(self.sample_url, headers=self.headers)
        etag = first.headers['ETag']
        headers = self.headers.copy()
        headers['If-None-Match'] = etag
        with mock.patch.dict(self.app.app.registry.settings,
                             [('cliquet.backoff', 10)]):
            response = self.app.get(self.sample_url, headers=headers,
                                    status=304)
        self.assertIn('Backoff', response.headers)

    def test_backoff_header_is_present_on_error_responses(self):
        with mock.patch.dict(
                self.app.app.registry.settings,
                [('cliquet.backoff', 10)]):
            response = self.app.get(self.sample_url,
                                    headers=self.headers, status=200)
            self.assertIn('Backoff', response.headers)
            self.assertEquals(response.headers['Backoff'], '10')

    def test_404_is_valid_formatted_error(self):
        response = self.app.get('/unknown', status=404)
        self.assertFormattedError(
            response, 404, ERRORS.MISSING_RESOURCE, "Not Found",
            "The resource your are looking for could not be found.")

    def test_401_is_valid_formatted_error(self):
        response = self.app.get(self.sample_url, status=401)
        self.assertFormattedError(
            response, 401, ERRORS.MISSING_AUTH_TOKEN, "Unauthorized",
            "Please authenticate yourself to use this endpoint.")

    @authorize(False)
    def test_403_is_valid_formatted_error(self):
        response = self.app.get(self.sample_url,
                                headers=self.headers, status=403)
        self.assertFormattedError(
            response, 403, ERRORS.FORBIDDEN, "Forbidden",
            "This user cannot access this resource.")

    def test_405_is_valid_formatted_error(self):
        response = self.app.patch(self.sample_url,
                                  headers=self.headers, status=405)
        self.assertFormattedError(
            response, 405, ERRORS.METHOD_NOT_ALLOWED, "Method Not Allowed",
            "Method not allowed on this endpoint.")

    def test_500_is_valid_formatted_error(self):
        with mock.patch(
                'cliquet.tests.testapp.views.Mushroom._extract_filters',
                side_effect=ValueError):
            response = self.app.get(self.sample_url,
                                    headers=self.headers, status=500)
        self.assertFormattedError(
            response, 500, ERRORS.UNDEFINED, "Internal Server Error",
            "A programmatic error occured, developers have been informed.",
            "https://github.com/mozilla-services/cliquet/issues/")

    def test_info_link_in_error_responses_can_be_configured(self):
        with mock.patch(
                'cliquet.tests.testapp.views.Mushroom._extract_filters',
                side_effect=ValueError):
            link = "https://github.com/mozilla-services/kinto/issues/"
            app = self._get_test_app({
                'cliquet.error_info_link': link
            })
            response = app.get(self.sample_url,
                               headers=self.headers, status=500)
        self.assertFormattedError(
            response, 500, ERRORS.UNDEFINED, "Internal Server Error",
            "A programmatic error occured, developers have been informed.",
            link)

    def test_503_is_valid_formatted_error(self):
        with mock.patch(
                'cliquet.tests.testapp.views.Mushroom._extract_filters',
                side_effect=httpexceptions.HTTPServiceUnavailable):
            response = self.app.get(self.sample_url,
                                    headers=self.headers, status=503)
        self.assertFormattedError(
            response, 503, ERRORS.BACKEND, "Service Unavailable",
            "Service unavailable due to high load, please retry later.")
        self.assertIn("Retry-After", response.headers)

    def test_500_provides_traceback_on_server(self):
        mock_traceback = mock.patch('logging.traceback.print_exception')
        with mock.patch(
                'cliquet.tests.testapp.views.Mushroom._extract_filters',
                side_effect=ValueError):
            with mock_traceback as mocked_traceback:
                self.app.get(self.sample_url, headers=self.headers, status=500)
                self.assertTrue(mocked_traceback.called)
                self.assertEqual(ValueError,
                                 mocked_traceback.call_args[0][0])


class RedirectViewTest(FormattedErrorMixin, BaseWebTest, unittest.TestCase):
    api_prefix = ''

    def test_do_not_redirect_to_version_if_not_supported_version(self):
        resp = self.app.get('/v1/', status=404)
        self.assertFormattedError(
            resp, 404, ERRORS.VERSION_NOT_AVAILABLE, "Not Found",
            "The requested protocol version is not available "
            "on this server.")

    def test_redirect_to_version(self):
        # GET on the hello view.
        response = self.app.get('/')
        self.assertEqual(response.status_int, 307)
        self.assertEqual(response.location,
                         'http://localhost/v0/')

        # GET on the fields view.
        response = self.app.get('/mushrooms')
        self.assertEqual(response.status_int, 307)
        self.assertEqual(response.location,
                         'http://localhost/v0/mushrooms')

    def test_do_not_redirect_to_version_if_disabled_in_settings(self):
        # GET on the hello view.
        app = self._get_test_app({
            'cliquet.version_prefix_redirect_enabled': False
        })

        app.get('/', status=404)

    def test_querystring_is_preserved_during_redirection(self):
        response = self.app.get('/home/articles?_since=42')
        self.assertEqual(response.location,
                         'http://localhost/v0/home/articles?_since=42')
