"""Verify the WordPress legacy-hash verifiers against REAL WordPress 6.8.5 output.

The fixtures below were generated by ``wp_hash_password()`` and phpass
``PasswordHash`` on a live WordPress 6.8.5 install (2026-08-25) for known TEST
passwords — never real user data. They are ground-truth vectors: if these
verify, the migration accepts real owners' existing passwords without a reset.

These are ``SimpleTestCase``s — they exercise the hashers through Django's
``check_password`` machinery and touch no database.
"""
import base64
import hashlib
import hmac

import bcrypt
from django.contrib.auth.hashers import check_password, identify_hasher
from django.test import SimpleTestCase

# (plaintext, real WordPress 6.8 "$wp$" hash, real phpass "$P$" hash)
WP_FIXTURES = [
    (
        "PelicanBrief-2026-xK",
        "$wp$2y$10$nbb8oPbJemN6VnNDmT6T9..dbWj/3URu2MpVpIKJfouHUbH5/DtyC",
        "$P$BVzHgQNszxzuytnIrqxhI3Q6VaFd6N1",
    ),
    (
        "aB3 dE6 fG9",
        "$wp$2y$10$O75AucSEJpvgh6a0.laiB.i3rOVhNsCprEuDRUN.q2lsfMvquPJPm",
        "$P$BcTjiXIbBs2WyNdemaMaCyDsJ0kTk11",
    ),
    (
        "passwoerd-cafe-9",
        "$wp$2y$10$eODnS72zdP.p6ZKZ/pXgvumINo4QcFKRrtBWVOsBLZaizXcnDYYOe",
        "$P$B/C5kvA.4IhgytvJ6hbVJ0QKwqqjpr/",
    ),
]


def _stored_bcrypt(wp_hash):
    return "wp_bcrypt$" + wp_hash


def _stored_phpass(wp_hash):
    return "wp_phpass$" + wp_hash


class WordPressBcryptHasherTests(SimpleTestCase):
    def test_verifies_real_wp68_hashes(self):
        for plain, wp, _phpass in WP_FIXTURES:
            self.assertTrue(
                check_password(plain, _stored_bcrypt(wp)),
                f"WP 6.8 hash should verify for {plain!r}",
            )

    def test_rejects_wrong_password(self):
        for plain, wp, _phpass in WP_FIXTURES:
            self.assertFalse(check_password(plain + "x", _stored_bcrypt(wp)))
            self.assertFalse(check_password("", _stored_bcrypt(wp)))

    def test_does_not_trim_on_verify(self):
        # wp_check_password() does NOT trim (only wp_hash_password() does), so a
        # trailing space must fail — matching WordPress login behaviour exactly.
        plain, wp, _phpass = WP_FIXTURES[0]
        self.assertFalse(check_password(plain + " ", _stored_bcrypt(wp)))

    def test_vanilla_2y_bcrypt(self):
        plain = "vanilla-bcrypt-9"
        raw = bcrypt.hashpw(plain.encode(), bcrypt.gensalt(rounds=10)).decode()
        self.assertTrue(check_password(plain, _stored_bcrypt(raw)))
        self.assertFalse(check_password(plain + "x", _stored_bcrypt(raw)))

    def test_wp68_prehash_is_hmac_not_plain_sha384(self):
        # Guard the classic mistake: the pre-hash is HMAC-SHA-384 with the static
        # key b"wp-sha384", not plain SHA-384. Plain SHA-384 must NOT verify.
        plain, wp, _phpass = WP_FIXTURES[0]
        inner = wp[3:].encode("ascii")
        plain_sha = base64.b64encode(hashlib.sha384(plain.encode()).digest())
        hmac_sha = base64.b64encode(
            hmac.new(b"wp-sha384", plain.encode(), hashlib.sha384).digest()
        )
        self.assertFalse(bcrypt.checkpw(plain_sha, inner))
        self.assertTrue(bcrypt.checkpw(hmac_sha, inner))

    def test_identifies_hasher(self):
        _plain, wp, _phpass = WP_FIXTURES[0]
        self.assertEqual(identify_hasher(_stored_bcrypt(wp)).algorithm, "wp_bcrypt")


class WordPressPhpassHasherTests(SimpleTestCase):
    def test_verifies_real_phpass_hashes(self):
        for plain, _wp, phpass in WP_FIXTURES:
            self.assertTrue(
                check_password(plain, _stored_phpass(phpass)),
                f"phpass hash should verify for {plain!r}",
            )

    def test_rejects_wrong_password(self):
        for plain, _wp, phpass in WP_FIXTURES:
            self.assertFalse(check_password(plain + "x", _stored_phpass(phpass)))
            self.assertFalse(check_password("", _stored_phpass(phpass)))

    def test_identifies_hasher(self):
        _plain, _wp, phpass = WP_FIXTURES[0]
        self.assertEqual(identify_hasher(_stored_phpass(phpass)).algorithm, "wp_phpass")


class RehashOnLoginTests(SimpleTestCase):
    """A successful check against a legacy WP hash must ask Django to upgrade the
    stored hash to the preferred (Argon2) hasher — the just-in-time migration."""

    def test_wp68_hash_triggers_rehash(self):
        plain, wp, _phpass = WP_FIXTURES[0]
        upgraded = {}

        def setter(_raw):
            upgraded["called"] = True

        self.assertTrue(check_password(plain, _stored_bcrypt(wp), setter=setter))
        self.assertTrue(upgraded.get("called"), "legacy WP hash should trigger rehash")

    def test_phpass_hash_triggers_rehash(self):
        plain, _wp, phpass = WP_FIXTURES[0]
        upgraded = {}

        def setter(_raw):
            upgraded["called"] = True

        self.assertTrue(check_password(plain, _stored_phpass(phpass), setter=setter))
        self.assertTrue(upgraded.get("called"), "legacy phpass hash should trigger rehash")
