"""
THE httpOnly REFRESH COOKIE, AND THE CONTRACT IT MUST NOT DISTURB.

Two halves, and the second is the one that would cost real money to get wrong.
The PWA's behaviour is new and only the PWA can break. The desktop client's
behaviour is old, shipped, and shared with the website — and every endpoint the
cookie touches is one of theirs.

`test_refresh_contract.py` is the sibling of this file: it asserts a refresh
token survives being used. This one asserts that asking for a cookie changes
what a browser gets and nothing about what anybody else gets.
"""

from django.test import TestCase
from django.urls import reverse
from rest_framework_simplejwt.tokens import RefreshToken

from users.cookies import REFRESH_COOKIE
from users.models import CustomUser

WEB = {'HTTP_X_LIFEY_CLIENT': 'web'}
PASSWORD = 'pw-for-tests-123'


class CookieOptInTests(TestCase):
    def setUp(self):
        self.user = CustomUser.objects.create_user('cookie@example.com', PASSWORD)
        self.login_url = reverse('users:login')

    def _login(self, **extra):
        return self.client.post(
            self.login_url,
            data={'email': 'cookie@example.com', 'password': PASSWORD},
            content_type='application/json',
            **extra,
        )

    def test_a_caller_without_the_header_gets_the_old_response(self):
        """
        THE DESKTOP CONTRACT, UNCHANGED. This is the assertion that makes the
        whole feature safe to deploy: the app and the website send no header,
        so they must not be able to tell the cookie exists.
        """
        response = self._login()

        self.assertEqual(response.status_code, 200)
        self.assertIn('refresh', response.json())
        self.assertIn('access', response.json())
        self.assertNotIn(REFRESH_COOKIE, response.cookies)

    def test_the_web_client_gets_a_cookie_instead_of_the_token(self):
        response = self._login(**WEB)

        self.assertEqual(response.status_code, 200)
        self.assertIn(REFRESH_COOKIE, response.cookies)
        # THE WHOLE POINT. Sending both would hand the page the very string the
        # cookie exists to keep away from it.
        self.assertNotIn('refresh', response.json())
        self.assertIn('access', response.json())

    def test_the_cookie_is_httponly_and_samesite_lax(self):
        """
        `SameSite` is not a nicety here. `CORS_ALLOW_ALL_ORIGINS` is on for the
        Electron `null` origin, so with credentials enabled every origin is
        told credentials are permitted — and the only thing keeping this cookie
        away from a hostile page is the browser's own cross-site rule.
        """
        cookie = self._login(**WEB).cookies[REFRESH_COOKIE]

        self.assertTrue(cookie['httponly'])
        self.assertEqual(cookie['samesite'], 'Lax')
        self.assertEqual(cookie['path'], '/api/')

    def test_register_sets_the_cookie_too(self):
        response = self.client.post(
            reverse('users:register'),
            data={'email': 'fresh@example.com', 'password': PASSWORD},
            content_type='application/json',
            **WEB,
        )

        self.assertEqual(response.status_code, 201)
        self.assertIn(REFRESH_COOKIE, response.cookies)
        self.assertNotIn('refresh', response.json())


class CookieRefreshTests(TestCase):
    def setUp(self):
        self.user = CustomUser.objects.create_user('refresh@example.com', PASSWORD)
        self.url = reverse('token-refresh')

    def test_the_cookie_is_enough_to_refresh(self):
        """A PWA has never seen its refresh token, so it can send no body."""
        self.client.cookies[REFRESH_COOKIE] = str(RefreshToken.for_user(self.user))

        response = self.client.post(self.url, data={}, content_type='application/json')

        self.assertEqual(response.status_code, 200)
        self.assertIn('access', response.json())

    def test_a_body_still_works_and_is_preferred(self):
        """
        The desktop path, unchanged — and the one caller that could hold both.
        A browser that signed in before the cookie existed still has a token in
        memory, and the body is the session it is actually trying to refresh.
        """
        body_token = RefreshToken.for_user(self.user)
        other = CustomUser.objects.create_user('other@example.com', PASSWORD)
        self.client.cookies[REFRESH_COOKIE] = str(RefreshToken.for_user(other))

        response = self.client.post(
            self.url, data={'refresh': str(body_token)}, content_type='application/json'
        )

        self.assertEqual(response.status_code, 200)

    def test_no_body_and_no_cookie_is_still_a_400(self):
        response = self.client.post(self.url, data={}, content_type='application/json')

        self.assertEqual(response.status_code, 400)

    def test_a_junk_cookie_is_rejected_rather_than_ignored(self):
        self.client.cookies[REFRESH_COOKIE] = 'not-a-token'

        response = self.client.post(self.url, data={}, content_type='application/json')

        self.assertEqual(response.status_code, 401)


class CookieLogoutTests(TestCase):
    def setUp(self):
        self.user = CustomUser.objects.create_user('bye@example.com', PASSWORD)
        self.url = reverse('users:logout')

    def test_logout_blacklists_the_token_in_the_cookie(self):
        """
        Without this a PWA logout blacklists nothing: it has no copy of its own
        refresh token to put in the body, so a thirty-day credential would stay
        alive on what may be a shared machine.
        """
        token = RefreshToken.for_user(self.user)
        self.client.cookies[REFRESH_COOKIE] = str(token)

        response = self.client.post(
            self.url,
            data={},
            content_type='application/json',
            HTTP_AUTHORIZATION=f'Bearer {token.access_token}',
        )
        self.assertEqual(response.status_code, 205)

        refreshed = self.client.post(
            reverse('token-refresh'),
            data={'refresh': str(token)},
            content_type='application/json',
        )
        self.assertEqual(refreshed.status_code, 401)

    def test_logout_clears_the_cookie_even_when_there_was_none(self):
        token = RefreshToken.for_user(self.user)

        response = self.client.post(
            self.url,
            data={},
            content_type='application/json',
            HTTP_AUTHORIZATION=f'Bearer {token.access_token}',
        )

        self.assertEqual(response.cookies[REFRESH_COOKIE].value, '')
