"""The account menu: profile, password change, sign out.

The point of these pages is that an owner never has to email the office to
change their own details — the same reason the reset flow exists.
"""
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

User = get_user_model()


class ProfileTests(TestCase):
    def setUp(self):
        self.owner = User.objects.create_user(
            "owner", email="owner@example.com", password="pw-owner-12345",
            first_name="Pat", last_name="Reid",
        )
        self.other = User.objects.create_user(
            "other", email="other@example.com", password="pw-other-12345"
        )

    # ---- access ----
    def test_anonymous_is_sent_to_sign_in(self):
        response = self.client.get(reverse("accounts:profile"), secure=True)
        self.assertEqual(response.status_code, 302)
        self.assertIn("/accounts/login/", response.url)

    def test_an_owner_can_open_their_profile(self):
        self.client.force_login(self.owner)
        response = self.client.get(reverse("accounts:profile"), secure=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "owner@example.com")

    # ---- editing ----
    def test_saving_name_and_email(self):
        self.client.force_login(self.owner)
        response = self.client.post(reverse("accounts:profile"), {
            "first_name": "Patricia", "last_name": "Reid-Jones",
            "email": "patricia@example.com",
        }, secure=True)
        self.assertEqual(response.status_code, 302)
        self.owner.refresh_from_db()
        self.assertEqual(self.owner.first_name, "Patricia")
        self.assertEqual(self.owner.email, "patricia@example.com")

    def test_an_email_already_in_use_is_refused(self):
        """A duplicate address locks BOTH accounts out, not just the new one.

        Sign-in accepts an email, and the backend refuses to guess when two
        accounts share one — so this has to be caught before it is saved.
        """
        self.client.force_login(self.owner)
        response = self.client.post(reverse("accounts:profile"), {
            "first_name": "Pat", "last_name": "Reid", "email": "OTHER@example.com",
        }, secure=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Another account already uses")
        self.owner.refresh_from_db()
        self.assertEqual(self.owner.email, "owner@example.com")

    def test_keeping_your_own_email_is_not_a_clash(self):
        self.client.force_login(self.owner)
        self.client.post(reverse("accounts:profile"), {
            "first_name": "Pat", "last_name": "Reid", "email": "owner@example.com",
        }, secure=True)
        self.owner.refresh_from_db()
        self.assertEqual(self.owner.email, "owner@example.com")

    def test_email_is_required(self):
        self.client.force_login(self.owner)
        self.client.post(reverse("accounts:profile"), {
            "first_name": "Pat", "last_name": "Reid", "email": "",
        }, secure=True)
        self.owner.refresh_from_db()
        self.assertEqual(self.owner.email, "owner@example.com")

    def test_the_form_cannot_be_used_to_grant_staff(self):
        """Only the three declared fields are editable — nothing else posted counts."""
        self.client.force_login(self.owner)
        self.client.post(reverse("accounts:profile"), {
            "first_name": "Pat", "last_name": "Reid", "email": "owner@example.com",
            "is_staff": "on", "is_superuser": "on", "username": "admin",
        }, secure=True)
        self.owner.refresh_from_db()
        self.assertFalse(self.owner.is_staff)
        self.assertFalse(self.owner.is_superuser)
        self.assertEqual(self.owner.username, "owner")

    def test_one_owner_cannot_edit_another(self):
        """The view edits request.user, so there is no id to tamper with."""
        self.client.force_login(self.owner)
        self.client.post(reverse("accounts:profile"), {
            "first_name": "Hijacked", "last_name": "X", "email": "new@example.com",
        }, secure=True)
        self.other.refresh_from_db()
        self.assertNotEqual(self.other.first_name, "Hijacked")
        self.assertEqual(self.other.email, "other@example.com")

    # ---- password ----
    def test_changing_your_password(self):
        self.client.force_login(self.owner)
        response = self.client.post(reverse("password_change"), {
            "old_password": "pw-owner-12345",
            "new_password1": "brand-new-pass-9182",
            "new_password2": "brand-new-pass-9182",
        }, secure=True)
        self.assertEqual(response.status_code, 302)
        self.owner.refresh_from_db()
        self.assertTrue(self.owner.check_password("brand-new-pass-9182"))

    def test_the_current_password_is_required_to_change_it(self):
        self.client.force_login(self.owner)
        self.client.post(reverse("password_change"), {
            "old_password": "wrong-one",
            "new_password1": "brand-new-pass-9182",
            "new_password2": "brand-new-pass-9182",
        }, secure=True)
        self.owner.refresh_from_db()
        self.assertTrue(self.owner.check_password("pw-owner-12345"))

    # ---- the menu itself ----
    def test_the_menu_offers_all_three_actions(self):
        self.client.force_login(self.owner)
        body = self.client.get(reverse("documents:library"), secure=True).content.decode()
        self.assertIn(reverse("accounts:profile"), body)
        self.assertIn(reverse("password_change"), body)
        self.assertIn(reverse("logout"), body)
        self.assertIn("PR", body)          # initials on the avatar

    def test_the_menu_is_absent_when_signed_out(self):
        body = self.client.get(reverse("login"), secure=True).content.decode()
        self.assertNotIn("usermenu", body)

    def test_signing_out_still_needs_a_post(self):
        self.client.force_login(self.owner)
        self.assertEqual(self.client.get(reverse("logout"), secure=True).status_code, 405)
