from bluetooth_data_tools import get_cipher_for_irk, resolve_private_address

# A realistic, non-zero 128-bit IRK. The RPA vectors below were generated by
# encrypting ``b"\x00" * 13 + prand`` with this key under AES-ECB and appending
# the top 24 bits of the result as the hash — the same construction the resolver
# verifies. They are computed independently of the production helper so a
# byte-order or slicing regression in ``resolve_private_address`` is caught
# rather than mirrored.
IRK = bytes.fromhex("ec0234a357c8ad05341010a60a397d9b")

# (address, prand) pairs that resolve against ``IRK``. Each first octet has its
# top two bits set to 0b01, marking it as a resolvable private address.
VALID_RPAS = (
    "40:01:02:b2:12:da",
    "55:aa:33:1d:27:08",
    "7f:ff:fe:7e:0e:08",
    "40:00:00:45:0c:63",
)


def test_resolve_private_address():
    cipher = get_cipher_for_irk(b"\x00" * 16)

    assert resolve_private_address(cipher, "40:01:02:0a:c4:a6")
    assert resolve_private_address(cipher, "40:02:03:d2:74:ce")
    assert not resolve_private_address(cipher, "40:00:00:d2:74:ce")
    assert not resolve_private_address(cipher, "00:01:ff:a0:3a:76")


def test_resolve_with_nonzero_irk():
    """A realistic non-zero IRK resolves its own generated RPAs."""
    cipher = get_cipher_for_irk(IRK)
    for address in VALID_RPAS:
        assert resolve_private_address(cipher, address), address


def test_resolve_is_case_insensitive():
    """Uppercase hex in the address resolves the same as lowercase."""
    cipher = get_cipher_for_irk(IRK)
    assert resolve_private_address(cipher, "40:01:02:B2:12:DA")
    assert resolve_private_address(cipher, "40:01:02:b2:12:da")


def test_resolve_rejects_wrong_hash():
    """An RPA-typed address with a non-matching hash does not resolve."""
    cipher = get_cipher_for_irk(IRK)
    assert not resolve_private_address(cipher, "40:01:02:00:00:00")


def test_resolve_rejects_wrong_irk():
    """A valid RPA does not resolve against a different IRK."""
    cipher = get_cipher_for_irk(b"\x11" * 16)
    assert not resolve_private_address(cipher, "40:01:02:b2:12:da")


def test_resolve_rejects_non_rpa_address_types():
    """Only addresses whose top two bits are 0b01 are treated as RPAs.

    The hash half is left identical to a known-good RPA; flipping only the
    address-type bits in the first octet must short-circuit to ``False`` before
    the hash is ever compared.
    """
    cipher = get_cipher_for_irk(IRK)
    # 0x00 -> 0b00 (public / non-resolvable), 0x80 -> 0b10 (random static
    # reserved), 0xc0 -> 0b11 (static random). None are resolvable.
    assert not resolve_private_address(cipher, "00:01:02:b2:12:da")
    assert not resolve_private_address(cipher, "80:01:02:b2:12:da")
    assert not resolve_private_address(cipher, "c0:01:02:b2:12:da")


def test_resolve_full_rpa_type_range():
    """Every first octet in 0x40..0x7f is an RPA candidate (checked, not rejected).

    A candidate with a deliberately wrong hash returns ``False`` via the hash
    comparison rather than the address-type gate, while values outside the range
    are rejected by the gate. This pins the 0x40/0x7f boundary.
    """
    cipher = get_cipher_for_irk(IRK)
    for first in range(0x40, 0x80):
        address = f"{first:02x}:00:00:00:00:00"
        # Wrong hash, but still inside the RPA range -> reaches the comparison.
        assert not resolve_private_address(cipher, address), address
    # Just outside the range on either side.
    assert not resolve_private_address(cipher, "3f:00:00:00:00:00")
    assert not resolve_private_address(cipher, "80:00:00:00:00:00")


def test_cipher_is_reusable_across_addresses():
    """One cipher resolves many addresses — the consumer's steady-state pattern.

    ``resolve_private_address`` creates a fresh encryptor per call, so a single
    ``Cipher`` built once per IRK must stay usable across an unbounded number of
    resolutions (and re-resolving the same address stays stable).
    """
    cipher = get_cipher_for_irk(IRK)
    for _ in range(3):
        for address in VALID_RPAS:
            assert resolve_private_address(cipher, address), address
        assert not resolve_private_address(cipher, "40:01:02:00:00:00")
