import hashlib

from meshy.models import (
    Channel,
    Contact,
    ContactType,
    battery_icon_name,
    battery_percent_from_mv,
    snr_to_icon,
    snr_to_label,
)

# ─── Battery ──────────────────────────────────────────────────


class TestBatteryPercent:
    def test_full(self):
        assert battery_percent_from_mv(4200) == 100

    def test_above_full(self):
        assert battery_percent_from_mv(4500) == 100

    def test_empty(self):
        assert battery_percent_from_mv(3000) == 0

    def test_below_empty(self):
        assert battery_percent_from_mv(2500) == 0

    def test_midpoint(self):
        pct = battery_percent_from_mv(3800)
        assert pct == 50

    def test_interpolation_4100(self):
        pct = battery_percent_from_mv(4100)
        assert 90 <= pct <= 100


class TestBatteryIcon:
    def test_full(self):
        assert battery_icon_name(95) == "battery-level-100-symbolic"

    def test_low(self):
        assert battery_icon_name(10) == "battery-level-low-symbolic"

    def test_mid(self):
        assert battery_icon_name(50) == "battery-level-50-symbolic"


# ─── SNR ──────────────────────────────────────────────────────


class TestSNR:
    def test_excellent_sf12(self):
        assert snr_to_label(5.0, sf=12) == "Excellent"
        assert snr_to_icon(5.0, sf=12) == "strength-bars-1-symbolic"

    def test_very_poor_sf12(self):
        assert snr_to_label(-20.0, sf=12) == "Very poor"
        assert snr_to_icon(-20.0, sf=12) == "strength-bars-5-symbolic"

    def test_unknown_sf_uses_sf12(self):
        assert snr_to_label(5.0, sf=99) == "Excellent"


# ─── Contact ──────────────────────────────────────────────────


class TestContact:
    def test_public_key_hex(self):
        c = Contact(public_key=bytes(range(32)), name="Test")
        assert c.public_key_hex == bytes(range(32)).hex()

    def test_type_label(self):
        assert Contact(public_key=bytes(32), name="A", type=ContactType.CHAT).type_label == "Chat"
        assert (
            Contact(public_key=bytes(32), name="A", type=ContactType.REPEATER).type_label
            == "Repeater"
        )

    def test_path_label_flood(self):
        c = Contact(public_key=bytes(32), name="A", path_length=-1)
        assert c.path_label == "Flood"

    def test_path_label_direct(self):
        c = Contact(public_key=bytes(32), name="A", path_length=0)
        assert c.path_label == "Direct"

    def test_path_label_hops(self):
        c = Contact(public_key=bytes(32), name="A", path_length=3)
        assert c.path_label == "3 hops"

    def test_path_label_smart(self):
        c = Contact(public_key=bytes(32), name="A", smart_route_selection=True)
        assert c.path_label == "Smart"

    def test_path_hops_hash_size_1(self):
        c = Contact(
            public_key=bytes(32),
            name="A",
            path_length=3,
            path=bytes([0xAA, 0xBB, 0xCC]),
            path_hash_size=1,
        )
        assert c.path_hops == ["AA", "BB", "CC"]

    def test_path_hops_hash_size_2(self):
        c = Contact(
            public_key=bytes(32),
            name="A",
            path_length=2,
            path=bytes([0xAA, 0xBB, 0xCC, 0xDD]),
            path_hash_size=2,
        )
        assert c.path_hops == ["AABB", "CCDD"]

    def test_path_hops_empty(self):
        c = Contact(public_key=bytes(32), name="A", path_length=-1)
        assert c.path_hops == []

    def test_has_location_valid(self):
        c = Contact(public_key=bytes(32), name="A", latitude=50.0, longitude=14.0)
        assert c.has_location is True

    def test_has_location_none(self):
        c = Contact(public_key=bytes(32), name="A")
        assert c.has_location is False

    def test_has_location_zero_zone_filtered(self):
        c = Contact(public_key=bytes(32), name="A", latitude=0.5, longitude=0.5)
        assert c.has_location is False

    def test_has_location_out_of_range(self):
        c = Contact(public_key=bytes(32), name="A", latitude=100.0, longitude=14.0)
        assert c.has_location is False


# ─── Channel ─────────────────────────────────────────────────


class TestChannel:
    def test_is_empty(self):
        ch = Channel.empty(0)
        assert ch.is_empty is True

    def test_not_empty(self):
        ch = Channel(index=0, name="Test", psk=bytes(16))
        assert ch.is_empty is False

    def test_is_public_channel(self):
        psk = bytes.fromhex(Channel.PUBLIC_CHANNEL_PSK)
        ch = Channel(index=0, name="Public", psk=psk)
        assert ch.is_public_channel is True

    def test_is_hashtag_channel(self):
        name = "#test"
        psk = hashlib.sha256(name.encode()).digest()[:16]
        ch = Channel(index=0, name=name, psk=psk)
        assert ch.is_hashtag_channel is True

    def test_private_channel(self):
        ch = Channel(index=0, name="Private", psk=bytes(range(16)))
        assert ch.is_public_channel is False
        assert ch.is_hashtag_channel is False
        assert ch.channel_type == "Private"
