import unittest

from pyramid.config import Configurator

from .. import DualCookieCSRFStoragePolicy
from pyramid.interfaces import ICSRFStoragePolicy


class DummyRequest(object):
    registry = None
    session = None
    response_callbacks = None
    scheme = None

    def __init__(self, registry=None, session=None, scheme='http'):
        self.registry = registry
        self.session = session
        self.cookies = {}
        self.scheme = scheme
        self.response_callbacks = []

    def add_response_callback(self, callback):
        self.response_callbacks.append(callback)

    def response_callback(self, request, response):
        while len(self.response_callbacks):
            callback = self.response_callbacks.pop()
            callback(request, response)


class MockResponse(object):
    def __init__(self):
        self.headerlist = []


class TestCookieCSRFStoragePolicy_InsecureHttp(unittest.TestCase):
    """tests on the insecure channel"""

    def _makeOne(self, **kw):
        return DualCookieCSRFStoragePolicy(**kw)

    def test_register_cookie_csrf_policy(self):
        config = Configurator()
        config.set_csrf_storage_policy(self._makeOne())
        config.commit()
        policy = config.registry.queryUtility(ICSRFStoragePolicy)
        self.assertTrue(isinstance(policy, DualCookieCSRFStoragePolicy))

    def test_get_cookie_csrf_with_no_existing_cookie_sets_cookies(self):
        response = MockResponse()
        request = DummyRequest()

        policy = self._makeOne()
        token = policy.get_csrf_token(request)
        request.response_callback(request, response)
        self.assertEqual(
            response.headerlist,
            [('Set-Cookie', 'csrf_http={}; Path=/; SameSite=Lax'.format(token))]
        )

    def test_get_cookie_csrf_nondefault_samesite(self):
        response = MockResponse()
        request = DummyRequest()

        policy = self._makeOne(samesite=None)
        token = policy.get_csrf_token(request)
        request.response_callback(request, response)
        self.assertEqual(
            response.headerlist,
            [('Set-Cookie', 'csrf_http={}; Path=/'.format(token))],
        )
        
    def test_existing_cookie_csrf_does_not_set_cookie(self):
        request = DummyRequest()
        request.cookies = {'csrf_http': 'e6f325fee5974f3da4315a8ccf4513d2'}

        policy = self._makeOne()
        token = policy.get_csrf_token(request)

        self.assertEqual(
            token,
            'e6f325fee5974f3da4315a8ccf4513d2'
        )
        self.assertEqual(len(request.response_callbacks), 0)

    def test_new_cookie_csrf_with_existing_cookie_sets_cookies(self):
        request = DummyRequest()
        request.cookies = {'csrf_http': 'e6f325fee5974f3da4315a8ccf4513d2'}

        policy = self._makeOne()
        token = policy.new_csrf_token(request)

        response = MockResponse()
        request.response_callback(request, response)
        self.assertEqual(
            response.headerlist,
            [('Set-Cookie', 'csrf_http={}; Path=/; SameSite=Lax'.format(token))]
        )

    def test_get_csrf_token_returns_the_new_token(self):
        request = DummyRequest()
        request.cookies = {'csrf_http': 'foo'}

        policy = self._makeOne()
        self.assertEqual(policy.get_csrf_token(request), 'foo')

        token = policy.new_csrf_token(request)
        self.assertNotEqual(token, 'foo')
        self.assertEqual(token, policy.get_csrf_token(request))

    def test_check_csrf_token(self):
        request = DummyRequest()

        policy = self._makeOne()
        self.assertFalse(policy.check_csrf_token(request, 'foo'))

        request.cookies = {'csrf_http': 'foo'}
        self.assertTrue(policy.check_csrf_token(request, 'foo'))
        self.assertFalse(policy.check_csrf_token(request, 'bar'))


class TestCookieCSRFStoragePolicy_SecureHTTPS(unittest.TestCase):
    """tests on the SECURE HTTPS"""

    def _makeOne(self, **kw):
        return DualCookieCSRFStoragePolicy(**kw)

    def test_register_cookie_csrf_policy(self):
        config = Configurator()
        config.set_csrf_storage_policy(self._makeOne())
        config.commit()
        policy = config.registry.queryUtility(ICSRFStoragePolicy)
        self.assertTrue(isinstance(policy, DualCookieCSRFStoragePolicy))

    def test_get_cookie_csrf_with_no_existing_cookie_sets_cookies(self):
        response = MockResponse()
        request = DummyRequest(scheme='https')

        policy = self._makeOne()
        token_secure = policy.get_csrf_token(request)
        token_http = policy.get_csrf_token_scheme(request, 'http')
        request.response_callback(request, response)

        self.assertEqual(
            response.headerlist,
            [('Set-Cookie', 'csrf_https={}; Path=/; secure; SameSite=Lax'.format(token_secure)),
             ('Set-Cookie', 'csrf_http={}; Path=/; SameSite=Lax'.format(token_http))
             ]
        )

    def test_get_cookie_csrf_nondefault_samesite(self):
        response = MockResponse()
        request = DummyRequest(scheme='https')

        policy = self._makeOne(samesite=None)
        token_secure = policy.get_csrf_token(request)
        token_http = policy.get_csrf_token_scheme(request, 'http')
        request.response_callback(request, response)
        self.assertEqual(
            response.headerlist,
            [('Set-Cookie', 'csrf_https={}; Path=/; secure'.format(token_secure)),
             ('Set-Cookie', 'csrf_http={}; Path=/'.format(token_http))
             ]
        )

    def test_existing_cookie_csrf_does_not_set_cookie(self):
        request = DummyRequest(scheme='https')
        request.cookies = {'csrf_https': 'e6f325fee5974f3da4315a8ccf4513d2'}

        policy = self._makeOne()
        token_secure = policy.get_csrf_token(request)

        self.assertEqual(
            token_secure,
            'e6f325fee5974f3da4315a8ccf4513d2'
        )
        self.assertEqual(len(request.response_callbacks), 0)

    def test_new_cookie_csrf_with_existing_cookie_sets_cookies(self):
        request = DummyRequest(scheme='https')
        request.cookies = {'csrf_https': 'e6f325fee5974f3da4315a8ccf4513d2'}

        policy = self._makeOne()
        token_secure = policy.new_csrf_token(request)
        token_http = policy.get_csrf_token_scheme(request, 'http')

        response = MockResponse()
        request.response_callback(request, response)
        self.assertEqual(
            response.headerlist,
            [('Set-Cookie', 'csrf_https={}; Path=/; secure; SameSite=Lax'.format(token_secure)),
             ('Set-Cookie', 'csrf_http={}; Path=/; SameSite=Lax'.format(token_http))
             ]
        )

    def test_get_csrf_token_returns_the_new_token(self):
        request = DummyRequest(scheme='https')
        request.cookies = {'csrf_https': 'foo'}

        policy = self._makeOne()
        self.assertEqual(policy.get_csrf_token(request), 'foo')

        token_secure = policy.new_csrf_token(request)
        self.assertNotEqual(token_secure, 'foo')
        self.assertEqual(token_secure, policy.get_csrf_token(request))

    def test_check_csrf_token(self):
        request = DummyRequest(scheme='https')

        policy = self._makeOne()
        self.assertFalse(policy.check_csrf_token(request, 'foo'))

        request.cookies = {'csrf_https': 'foo'}
        self.assertTrue(policy.check_csrf_token(request, 'foo'))
        self.assertFalse(policy.check_csrf_token(request, 'bar'))
