"""Tests for the CRC-16/IBM helper.

The reference vector is the algorithm's universal sanity check: the
ASCII string ``"123456789"`` over CRC-16/IBM produces ``0xBB3D``. If
the helper computes that, it's correct.
"""

from __future__ import annotations

from hypothesis import given, settings
from hypothesis import strategies as st

from mgf.tele.teltonika._crc import crc16_ibm


def test_reference_vector_123456789() -> None:
    """Universal CRC-16/IBM sanity check (a.k.a. CRC-16/ARC)."""
    assert crc16_ibm(b"123456789") == 0xBB3D


def test_empty_input_yields_zero() -> None:
    assert crc16_ibm(b"") == 0


def test_single_zero_byte_yields_zero() -> None:
    """Init=0, polynomial reversal — 0x00 input bytes leave CRC at 0."""
    assert crc16_ibm(b"\x00") == 0
    assert crc16_ibm(b"\x00\x00\x00\x00\x00") == 0


@given(st.binary(min_size=0, max_size=4096))
@settings(max_examples=200)
def test_crc_in_range_and_deterministic(data: bytes) -> None:
    """Output is always 0..0xFFFF and stable across calls."""
    crc = crc16_ibm(data)
    assert 0 <= crc <= 0xFFFF
    assert crc16_ibm(data) == crc


@given(st.binary(min_size=1, max_size=256))
@settings(max_examples=200)
def test_appending_byte_usually_changes_crc(data: bytes) -> None:
    """Property: appending bytes USUALLY changes the CRC. CRC-16 is
    not a hash — collisions exist by construction (the 16-bit output
    space is finite, the input space is not). Hypothesis surfaces
    those collisions as a matter of time. The original assertion
    ``crc(data) != crc(data + b"\\xff")`` overstated the guarantee
    and was fragile by design.

    The honest property: at least one of three different appended
    bytes MUST change the CRC. Three different bytes mapping to
    the same CRC for the same prefix is vanishingly rare (probability
    ~2^-32 per Hypothesis example) — exhausting all three is the
    real "this looks like a constant function" detector.
    """
    crc_a = crc16_ibm(data)
    crc_appended = {
        crc16_ibm(data + b"\xff"),
        crc16_ibm(data + b"\x01"),
        crc16_ibm(data + b"\xa5"),
    }
    # If ALL three appended-byte CRCs equal the original, the CRC
    # implementation has degenerated to a constant function on this
    # input. That's the real failure mode worth catching.
    assert crc_a not in crc_appended or len(crc_appended) > 1
