"""Password guessing at the sign-in form.

Every owner account still carries the password it was migrated with from
WordPress, and those sites were not under our control. An unthrottled login
form is the cheapest route to the documents, so the limits are asserted here
rather than trusted.
"""
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils import timezone

from accounts.models import LoginEvent
from accounts.throttle import MAX_PER_ACCOUNT, MAX_PER_ADDRESS, locked_out

User = get_user_model()
ATTACKER = "198.51.100.7"


class LoginThrottleTests(TestCase):
    def setUp(self):
        self.owner = User.objects.create_user("owner", password="correct-horse-1234")

    def attempt(self, password, ip=ATTACKER, username="owner"):
        return self.client.post(
            reverse("login"), {"username": username, "password": password},
            secure=True, REMOTE_ADDR=ip, HTTP_USER_AGENT="Mozilla/5.0 (Test)",
        )

    def test_a_correct_password_is_refused_once_locked_out(self):
        """The point of the lock: guessing must not pay off on the attempt
        that happens to be right."""
        for _ in range(MAX_PER_ACCOUNT):
            self.attempt("wrong")
        self.assertTrue(locked_out("owner", ATTACKER))

        self.attempt("correct-horse-1234")
        self.assertNotIn("_auth_user_id", self.client.session)

    def test_the_lock_lifts_when_the_failures_age_out(self):
        """A sliding window — nobody has to unlock an owner by hand."""
        old = timezone.now() - timezone.timedelta(hours=2)
        for _ in range(MAX_PER_ACCOUNT + 5):
            LoginEvent.objects.create(
                username="owner", succeeded=False, ip=ATTACKER, occurred_at=old)
        self.assertFalse(locked_out("owner", ATTACKER))

        self.attempt("correct-horse-1234")
        self.assertIn("_auth_user_id", self.client.session)

    def test_spraying_one_password_across_many_accounts_is_caught(self):
        """The attack a per-account limit never sees: a few tries each against
        hundreds of known usernames from one address."""
        for i in range(MAX_PER_ADDRESS):
            self.attempt("Summer2026!", username="owner%d" % i)
        self.assertTrue(locked_out("someone-else", ATTACKER))

    def test_one_persons_mistakes_do_not_lock_out_everyone(self):
        for _ in range(MAX_PER_ACCOUNT):
            self.attempt("wrong")
        self.assertFalse(locked_out("owner", "203.0.113.9"),
                         "a different address must be unaffected")

    def test_the_page_says_wait_rather_than_wrong_password(self):
        for _ in range(MAX_PER_ACCOUNT):
            self.attempt("wrong")
        body = self.attempt("wrong").content.decode()
        self.assertIn("Too many sign-in attempts", body)
        self.assertNotIn("didn&#x27;t match", body)

    @override_settings(LOGIN_THROTTLE_PER_ACCOUNT=10)
    def test_failures_are_recorded_with_the_address(self):
        """The throttle is built on LoginEvent, so the recording has to work."""
        self.attempt("wrong")
        event = LoginEvent.objects.filter(succeeded=False).latest("occurred_at")
        self.assertEqual(event.ip, ATTACKER)
        self.assertEqual(event.username, "owner")
