"""Tests for NMEA to TelemetryPoint converter."""

import pytest
from datetime import datetime, timezone
from uuid import UUID

from fishinfo.types import DataType, TelemetryPoint
from fishinfo.nmea.converter import NMEAConverter
from fishinfo.nmea.parser import NMEAParser
from fishinfo.nmea.ais import AISDecoder


class TestNMEAConverter:
    """Tests for NMEAConverter class."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(
            vessel_id=self.vessel_id,
            tenant_id=self.tenant_id,
            source="test_nmea",
        )
        self.parser = NMEAParser()

    def test_converter_initialization(self) -> None:
        """Test converter initialization."""
        assert self.converter.vessel_id == self.vessel_id
        assert self.converter.tenant_id == self.tenant_id
        assert self.converter.source == "test_nmea"

    def test_converter_with_string_uuids(self) -> None:
        """Test converter initialization with string UUIDs."""
        converter = NMEAConverter(
            vessel_id="12345678-1234-1234-1234-123456789abc",
            tenant_id="87654321-4321-4321-4321-cba987654321",
        )
        assert isinstance(converter.vessel_id, UUID)
        assert isinstance(converter.tenant_id, UUID)


class TestGGAConversion:
    """Tests for GGA sentence conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_gga_basic(self) -> None:
        """Test basic GGA to TelemetryPoint conversion."""
        sentence = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,47.0,M,,*47"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.data_type == DataType.POSITION
        assert point.latitude == pytest.approx(48.1173, rel=1e-4)
        assert point.longitude == pytest.approx(11.5167, rel=1e-4)
        assert point.value == 545.4  # altitude
        assert point.quality == 1
        assert point.satellites == 8
        assert point.hdop == 0.9
        assert point.source == "nmea_0183"
        assert point.sentence_type == "GGA"

    def test_convert_gga_no_fix(self) -> None:
        """Test GGA with no fix conversion."""
        sentence = "$GPGGA,123519,,,,,0,00,,,M,,M,,*4E"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert point.latitude is None
        assert point.longitude is None
        assert point.quality == 0


class TestRMCConversion:
    """Tests for RMC sentence conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_rmc_basic(self) -> None:
        """Test basic RMC to TelemetryPoint conversion."""
        sentence = "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"
        parsed = self.parser.parse(sentence)
        points = self.converter.to_telemetry_point(parsed)

        assert isinstance(points, list)
        assert len(points) == 3  # position, speed, course

        # Check position point
        pos_point = points[0]
        assert pos_point.data_type == DataType.POSITION
        assert pos_point.latitude == pytest.approx(48.1173, rel=1e-4)
        assert pos_point.longitude == pytest.approx(11.5167, rel=1e-4)
        assert pos_point.quality == 1  # Active status

        # Check speed point
        speed_point = points[1]
        assert speed_point.data_type == DataType.SPEED
        assert speed_point.value == 22.4
        assert speed_point.metadata["units"] == "knots"

        # Check course point
        course_point = points[2]
        assert course_point.data_type == DataType.COURSE
        assert course_point.value == 84.4
        assert course_point.metadata["units"] == "degrees_true"

    def test_convert_rmc_void_status(self) -> None:
        """Test RMC with void status conversion."""
        sentence = "$GPRMC,123519,V,,,,,,,230394,,*25"
        parsed = self.parser.parse(sentence)
        points = self.converter.to_telemetry_point(parsed)

        assert isinstance(points, list)
        # Should have no position point (lat/lon are None)
        assert len(points) == 0


class TestVTGConversion:
    """Tests for VTG sentence conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_vtg_basic(self) -> None:
        """Test basic VTG to TelemetryPoint conversion."""
        sentence = "$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48"
        parsed = self.parser.parse(sentence)
        points = self.converter.to_telemetry_point(parsed)

        assert isinstance(points, list)
        assert len(points) == 2  # speed, course

        # Check speed point
        speed_point = points[0]
        assert speed_point.data_type == DataType.SPEED
        assert speed_point.value == 5.5

        # Check course point
        course_point = points[1]
        assert course_point.data_type == DataType.COURSE
        assert course_point.value == 54.7


class TestHeadingConversion:
    """Tests for HDT/HDG sentence conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_hdt_basic(self) -> None:
        """Test basic HDT to TelemetryPoint conversion."""
        sentence = "$HEHDT,274.07,T*03"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.data_type == DataType.HEADING
        assert point.value == 274.07
        assert point.metadata["units"] == "degrees_true"

    def test_convert_hdg_basic(self) -> None:
        """Test basic HDG to TelemetryPoint conversion."""
        sentence = "$HEHDG,101.1,,,7.1,W*3C"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.data_type == DataType.HEADING
        assert point.value == 101.1
        assert point.metadata["units"] == "degrees_magnetic"
        assert point.metadata["variation"] == "-7.1"


class TestDepthConversion:
    """Tests for DBT/DPT sentence conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_dbt_basic(self) -> None:
        """Test basic DBT to TelemetryPoint conversion."""
        sentence = "$SDDBT,12.3,f,3.7,M,2.0,F*31"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.data_type == DataType.DEPTH
        assert point.value == 3.7  # meters
        assert point.metadata["units"] == "meters"

    def test_convert_dpt_basic(self) -> None:
        """Test basic DPT to TelemetryPoint conversion."""
        sentence = "$SDDPT,3.6,0.5,*5B"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.data_type == DataType.DEPTH
        assert point.value == 3.6
        assert point.metadata["offset"] == "0.5"


class TestWindConversion:
    """Tests for MWV sentence conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_mwv_basic(self) -> None:
        """Test basic MWV to TelemetryPoint conversion."""
        sentence = "$WIMWV,045.0,R,12.5,N,A*39"
        parsed = self.parser.parse(sentence)
        points = self.converter.to_telemetry_point(parsed)

        assert isinstance(points, list)
        assert len(points) == 2  # speed, direction

        # Check wind speed
        speed_point = points[0]
        assert speed_point.data_type == DataType.WIND_SPEED
        # 12.5 knots = 6.43 m/s
        assert speed_point.value == pytest.approx(6.43, rel=0.01)
        assert speed_point.metadata["units"] == "m/s"
        assert speed_point.metadata["reference"] == "relative"

        # Check wind direction
        dir_point = points[1]
        assert dir_point.data_type == DataType.WIND_DIRECTION
        assert dir_point.value == 45.0

    def test_convert_mwv_invalid_status(self) -> None:
        """Test MWV with invalid status."""
        sentence = "$WIMWV,045.0,R,12.5,N,V*20"
        parsed = self.parser.parse(sentence)
        points = self.converter.to_telemetry_point(parsed)

        assert isinstance(points, list)
        assert len(points) == 0  # Invalid status, no points


class TestTemperatureConversion:
    """Tests for MTW sentence conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_mtw_basic(self) -> None:
        """Test basic MTW to TelemetryPoint conversion."""
        sentence = "$YXMTW,15.5,C*1F"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.data_type == DataType.WATER_TEMPERATURE
        assert point.value == 15.5
        assert point.metadata["units"] == "celsius"


class TestAISConversion:
    """Tests for AIS message conversion."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.decoder = AISDecoder()

    def test_convert_ais_position_basic(self) -> None:
        """Test basic AIS position report conversion."""
        sentence = "!AIVDM,1,1,,B,13u@Dp0P00PlT>HMGN4?vhR00<02,0*66"
        ais_msg = self.decoder.decode(sentence)
        assert ais_msg is not None

        points = self.converter.to_telemetry_point(ais_msg)

        assert isinstance(points, list)
        assert len(points) >= 1  # At least position point

        # Check for position point
        pos_points = [p for p in points if p.data_type == DataType.AIS]
        assert len(pos_points) == 1
        assert pos_points[0].latitude is not None
        assert pos_points[0].longitude is not None
        assert "mmsi" in pos_points[0].metadata

    def test_convert_ais_static_data(self) -> None:
        """Test AIS static data conversion."""
        sentence1 = "!AIVDM,2,1,3,B,55?MbV02>H97PH97mR000<>0@T4@D0000000000015@T@10000000000000,0*7C"
        sentence2 = "!AIVDM,2,2,3,B,00000000000,2*20"

        self.decoder.decode(sentence1)
        ais_msg = self.decoder.decode(sentence2)
        assert ais_msg is not None

        point = self.converter.to_telemetry_point(ais_msg)

        assert isinstance(point, TelemetryPoint)
        assert point.data_type == DataType.AIS
        assert point.value_json is not None
        assert "mmsi" in point.value_json
        assert "vessel_name" in point.value_json


class TestTimestampOverride:
    """Tests for timestamp override functionality."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_timestamp_override(self) -> None:
        """Test overriding sentence timestamp."""
        sentence = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,47.0,M,,*47"
        parsed = self.parser.parse(sentence)

        custom_time = datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
        point = self.converter.to_telemetry_point(parsed, timestamp=custom_time)

        assert isinstance(point, TelemetryPoint)
        assert point.time == custom_time

    def test_timestamp_from_sentence(self) -> None:
        """Test using timestamp from sentence."""
        sentence = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,47.0,M,,*47"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.time is not None
        assert point.time.hour == 12
        assert point.time.minute == 35
        assert point.time.second == 19


class TestConversionEdgeCases:
    """Tests for conversion edge cases."""

    def setup_method(self) -> None:
        """Set up test fixtures."""
        self.vessel_id = UUID("12345678-1234-1234-1234-123456789abc")
        self.tenant_id = UUID("87654321-4321-4321-4321-cba987654321")
        self.converter = NMEAConverter(vessel_id=self.vessel_id, tenant_id=self.tenant_id)
        self.parser = NMEAParser()

    def test_convert_with_null_values(self) -> None:
        """Test conversion with null/missing values."""
        sentence = "$GPGGA,123519,,,,,0,00,,,M,,M,,*4E"
        parsed = self.parser.parse(sentence)
        point = self.converter.to_telemetry_point(parsed)

        assert isinstance(point, TelemetryPoint)
        assert point.latitude is None
        assert point.longitude is None
        assert point.value is None

    def test_convert_unsupported_type_raises(self) -> None:
        """Test conversion of unsupported sentence type raises error."""
        # Create a mock unsupported sentence
        class UnsupportedSentence:
            pass

        with pytest.raises(ValueError, match="Unsupported sentence type"):
            self.converter.to_telemetry_point(UnsupportedSentence())
