"""Tests for NTRIP client."""

import pytest

from lc29h.models import FixType, NtripConfig, Position
from lc29h.ntrip import NtripClient

# Real rtk2go.com source table response (subset for testing)
SAMPLE_SOURCE_TABLE = (
    "HTTP/1.1 200 OK\r\n"
    "Server: NTRIP Caster\r\n"
    "Content-Type: text/plain\r\n"
    "\r\n"
    "CAS;rtk2go.com;2101;NTRIP Caster;SNIP;0;USA;43.00;-76.00;0;0;0;none;none;B;N;0;\r\n"
    "STR;BOISE_ID;Boise;RTCM 3.2;1005(1),1074(1),1084(1),1094(1),1124(1);2;"
    "GPS+GLO+GAL+BDS;SNIP;USA;43.56;-116.48;0;0;sNTRIP;none;B;N;4800;\r\n"
    "STR;PORTLAND_OR;Portland;RTCM 3.3;1005(1),1077(1),1087(1),1097(1),1127(1);2;"
    "GPS+GLO+GAL+BDS;SNIP;USA;45.52;-122.68;1;0;sNTRIP;none;N;N;9600;\r\n"
    "STR;SHORT;x;RTCM 3;details;0;GPS;net;US;40.0;-100.0;0;sol;gen;comp;N;N;0;\r\n"
    "ENDSOURCETABLE\r\n"
)


class TestParseSourceTable:
    def test_parse_basic(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        mounts = client._parse_source_table(SAMPLE_SOURCE_TABLE)
        assert len(mounts) == 3

    def test_parse_mount_fields(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        mounts = client._parse_source_table(SAMPLE_SOURCE_TABLE)
        boise = next(m for m in mounts if m.name == "BOISE_ID")
        assert boise.format == "RTCM 3.2"
        assert "GPS" in boise.nav_system
        assert boise.country == "USA"
        assert abs(boise.latitude - 43.56) < 0.01
        assert abs(boise.longitude - (-116.48)) < 0.01
        assert boise.carrier == 2

    def test_parse_auth_and_fee(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        mounts = client._parse_source_table(SAMPLE_SOURCE_TABLE)
        portland = next(m for m in mounts if m.name == "PORTLAND_OR")
        assert portland.authentication == "N"
        assert portland.fee == "N"
        assert portland.nmea_required is True

    def test_parse_empty_response(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        mounts = client._parse_source_table("HTTP/1.1 200 OK\r\n\r\nENDSOURCETABLE\r\n")
        assert mounts == []

    def test_parse_cas_lines_ignored(self):
        """CAS and NET lines should be skipped, only STR lines parsed."""
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        response = (
            "HTTP/1.1 200 OK\r\n\r\n"
            "CAS;host;2101;caster;SNIP;0;USA;0;0;0;0;0;none;none;B;N;0;\r\n"
            "NET;NET1;net_op;auth;fee;url;url2;none\r\n"
            "STR;TEST;test;RTCM 3;details;0;GPS;net;US;40.0;-100.0;0;sol;gen;comp;N;N;0;\r\n"
            "ENDSOURCETABLE\r\n"
        )
        mounts = client._parse_source_table(response)
        assert len(mounts) == 1
        assert mounts[0].name == "TEST"

    def test_parse_malformed_str_skipped(self):
        """STR lines with too few fields should be skipped."""
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        response = "HTTP/1.1 200 OK\r\n\r\nSTR;short_entry;only_two\r\nENDSOURCETABLE\r\n"
        mounts = client._parse_source_table(response)
        assert mounts == []


class TestFormatGGA:
    def test_basic_gga(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        pos = Position(
            latitude=43.56,
            longitude=-116.48,
            altitude=830.0,
            fix_type=FixType.GPS_FIX,
            satellites=8,
            hdop=1.2,
        )
        gga = client.format_gga_for_vrs(pos)
        assert gga.startswith("$GPGGA,")
        assert gga.endswith("\r\n")
        assert ",N," in gga  # North latitude
        assert ",W," in gga  # West longitude
        assert ",1," in gga  # GPS fix type
        # Verify checksum format
        assert "*" in gga
        cs_part = gga.strip().split("*")[-1]
        assert len(cs_part) == 2

    def test_zero_position_returns_empty(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        pos = Position()  # 0,0 default
        gga = client.format_gga_for_vrs(pos)
        assert gga == ""

    def test_southern_hemisphere(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        pos = Position(
            latitude=-33.87,
            longitude=151.21,
            altitude=50.0,
            fix_type=FixType.GPS_FIX,
            satellites=6,
            hdop=1.5,
        )
        gga = client.format_gga_for_vrs(pos)
        assert ",S," in gga
        assert ",E," in gga

    def test_gga_checksum_valid(self):
        """Verify the generated GGA has a valid NMEA checksum."""
        from lc29h.protocol import validate_checksum

        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        pos = Position(
            latitude=43.56,
            longitude=-116.48,
            altitude=830.0,
            fix_type=FixType.GPS_FIX,
            satellites=8,
            hdop=1.2,
        )
        gga = client.format_gga_for_vrs(pos)
        assert validate_checksum(gga.strip())


class TestNtripClientState:
    def test_initial_state(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        assert not client.connected

    @pytest.mark.asyncio
    async def test_disconnect_when_not_connected(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        await client.disconnect()  # Should not raise
        assert not client.connected

    @pytest.mark.asyncio
    async def test_stream_rtcm_not_connected(self):
        config = NtripConfig(host="test", port=2101)
        client = NtripClient(config)
        with pytest.raises(ConnectionError):
            async for _ in client.stream_rtcm():
                pass
