"""The self-service password reset flow, end to end.

This is one of the features whose whole purpose is that an owner never has to
email anyone to get back into the site, so it is tested as a complete journey:
request -> email -> follow the link -> set a new password -> sign in with it.
"""
import re

from django.contrib.auth import get_user_model
from django.core import mail
from django.test import TestCase
from django.urls import reverse

User = get_user_model()


class PasswordResetFlowTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username="owner", email="owner@example.com", password="old-password-123"
        )

    def test_full_reset_journey(self):
        # 1. Owner asks for a reset.
        response = self.client.post(
            reverse("password_reset"), {"email": "owner@example.com"}, secure=True
        )
        self.assertEqual(response.status_code, 302)

        # 2. An email goes out, addressed to them, naming their username.
        self.assertEqual(len(mail.outbox), 1)
        message = mail.outbox[0]
        self.assertEqual(message.to, ["owner@example.com"])
        self.assertIn("owner", message.body)

        # 3. The link in the email works.
        match = re.search(r"/accounts/reset/[^/]+/[^/\s]+/", message.body)
        self.assertIsNotNone(match, "reset email must contain a reset link")
        link = match.group(0)
        follow = self.client.get(link, secure=True, follow=True)
        self.assertEqual(follow.status_code, 200)

        # 4. Set a new password at the redirected-to URL.
        confirm_url = follow.redirect_chain[-1][0] if follow.redirect_chain else link
        response = self.client.post(
            confirm_url,
            {"new_password1": "brand-new-pw-4567", "new_password2": "brand-new-pw-4567"},
            secure=True,
        )
        self.assertEqual(response.status_code, 302)

        # 5. The new password works and the old one does not.
        self.user.refresh_from_db()
        self.assertTrue(self.user.check_password("brand-new-pw-4567"))
        self.assertFalse(self.user.check_password("old-password-123"))

    def test_unknown_email_does_not_reveal_whether_an_account_exists(self):
        response = self.client.post(
            reverse("password_reset"), {"email": "nobody@example.com"}, secure=True
        )
        self.assertEqual(response.status_code, 302)  # same redirect as a real address
        self.assertEqual(len(mail.outbox), 0)  # but no mail is sent

    def test_reset_works_for_a_user_still_on_a_wordpress_hash(self):
        """A migrated owner who never logged in must still be able to reset."""
        from accounts.tests.test_hashers import WP_FIXTURES

        _plain, wp_hash, _phpass = WP_FIXTURES[0]
        self.user.password = "wp_bcrypt$" + wp_hash
        self.user.save(update_fields=["password"])

        self.client.post(reverse("password_reset"), {"email": "owner@example.com"}, secure=True)
        self.assertEqual(len(mail.outbox), 1)
        link = re.search(r"/accounts/reset/[^/]+/[^/\s]+/", mail.outbox[0].body).group(0)
        follow = self.client.get(link, secure=True, follow=True)
        confirm_url = follow.redirect_chain[-1][0]
        self.client.post(
            confirm_url,
            {"new_password1": "fresh-password-789", "new_password2": "fresh-password-789"},
            secure=True,
        )
        self.user.refresh_from_db()
        self.assertTrue(self.user.check_password("fresh-password-789"))
