from __future__ import annotations

from unittest.mock import patch

from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import User
from django.core.signing import TimestampSigner

from hc.test import BaseTestCase


class ChangeEmailVerifyTestCase(BaseTestCase):
    def setUp(self) -> None:
        super().setUp()
        self.profile.token = make_password("secret-token", "login")
        self.profile.save()

        self.checks_url = f"/projects/{self.project.code}/checks/"

    def _url(self, expired: bool = False) -> str:
        payload = {
            "u": self.alice.username,
            "t": TimestampSigner().sign("secret-token"),
            "e": "alice+new@example.org",
        }

        if expired:
            with patch("django.core.signing.TimestampSigner.timestamp") as mock_ts:
                mock_ts.return_value = "1kHR5c"
                signed = TimestampSigner().sign_object(payload)
        else:
            signed = TimestampSigner().sign_object(payload)

        return f"/accounts/change_email/{signed}/"

    def test_it_works(self) -> None:
        r = self.client.post(self._url())
        self.assertRedirects(r, self.checks_url)

        # Alice's email should have been updated, and password cleared
        self.alice.refresh_from_db()
        self.assertEqual(self.alice.email, "alice+new@example.org")
        self.assertFalse(self.alice.has_usable_password())

        # After login, token should be blank
        self.profile.refresh_from_db()
        self.assertEqual(self.profile.token, "")

    def test_it_handles_get(self) -> None:
        r = self.client.get(self._url())
        self.assertContains(r, "You are about to log into")

        # Alice's email should have *not* been changed yet
        self.alice.refresh_from_db()
        self.assertEqual(self.alice.email, "alice@example.org")

    def test_it_handles_get_with_cookie(self) -> None:
        self.client.cookies["auto-login"] = "1"
        r = self.client.get(self._url())
        self.assertRedirects(r, self.checks_url)

    def test_it_handles_expired_link(self) -> None:
        r = self.client.post(self._url(expired=True))
        self.assertContains(r, "The link you just used is incorrect.")

    def test_it_handles_bad_payload(self) -> None:
        r = self.client.post("/accounts/change_email/bad-payload/")
        self.assertContains(r, "The link you just used is incorrect.")

    def test_it_handles_unavailable_email(self) -> None:
        # Make the target address unavailable
        User.objects.create(email="alice+new@example.org")

        r = self.client.post(self._url(), follow=True)
        self.assertRedirects(r, "/accounts/login/")
        self.assertContains(r, "incorrect or expired")

        # Alice's email should have *not* been updated
        self.alice.refresh_from_db()
        self.assertEqual(self.alice.email, "alice@example.org")