"""Tests for baud rate auto-detection module."""

from unittest.mock import AsyncMock, patch

import pytest

from lc29h.detect import DetectionResult, _probe_baud, detect_receiver

from .conftest import _fix_checksum

VERNO_RESPONSE = _fix_checksum(
    "$PQTMVERNO,LC29HBSNR11A01S,2024/09/15,10:30:00*00"
)


class _MockTransport:
    """Minimal mock for detection probing."""

    def __init__(self, response: str | None = None):
        self._response = response

    async def connect(self):
        pass

    async def disconnect(self):
        pass

    async def query(self, cmd, prefix, timeout=2.0):
        return self._response


class TestProbeBaud:
    @pytest.mark.asyncio
    async def test_successful_detection(self):
        mock = _MockTransport(VERNO_RESPONSE)
        with patch("lc29h.detect.SerialTransport", return_value=mock):
            result = await _probe_baud("/dev/ttyUSB2", 115200)
        assert result is not None
        assert result.port == "/dev/ttyUSB2"
        assert result.baud == 115200
        assert result.model == "LC29HBS"
        assert result.firmware == "LC29HBSNR11A01S"

    @pytest.mark.asyncio
    async def test_no_response(self):
        mock = _MockTransport(None)
        with patch("lc29h.detect.SerialTransport", return_value=mock):
            result = await _probe_baud("/dev/ttyUSB2", 9600)
        assert result is None

    @pytest.mark.asyncio
    async def test_invalid_checksum(self):
        bad_line = "$PQTMVERNO,LC29HBSNR11A01S,2024/09/15,10:30:00*FF"
        mock = _MockTransport(bad_line)
        with patch("lc29h.detect.SerialTransport", return_value=mock):
            result = await _probe_baud("/dev/ttyUSB2", 115200)
        assert result is None

    @pytest.mark.asyncio
    async def test_non_lc29h_model(self):
        non_lc29h = _fix_checksum("$PQTMVERNO,FOOBAR123,2024/01/01,00:00:00*00")
        mock = _MockTransport(non_lc29h)
        with patch("lc29h.detect.SerialTransport", return_value=mock):
            result = await _probe_baud("/dev/ttyUSB2", 115200)
        assert result is None

    @pytest.mark.asyncio
    async def test_connection_failure(self):
        mock = _MockTransport()
        mock.connect = AsyncMock(side_effect=OSError("Port busy"))
        with patch("lc29h.detect.SerialTransport", return_value=mock):
            result = await _probe_baud("/dev/ttyUSB2", 115200)
        assert result is None


class TestDetectReceiver:
    @pytest.mark.asyncio
    async def test_finds_at_first_baud(self):
        mock = _MockTransport(VERNO_RESPONSE)
        with patch("lc29h.detect.SerialTransport", return_value=mock):
            result = await detect_receiver("/dev/ttyUSB2", bauds=[115200, 9600])
        assert result is not None
        assert result.baud == 115200

    @pytest.mark.asyncio
    async def test_finds_at_second_baud(self):
        class _BaudSpecificTransport:
            def __init__(self, port, baud):
                self.baud = baud

            async def connect(self):
                pass

            async def disconnect(self):
                pass

            async def query(self, cmd, prefix, timeout=2.0):
                # Only respond at 9600
                if self.baud == 9600:
                    return VERNO_RESPONSE
                return None

        with patch("lc29h.detect.SerialTransport", _BaudSpecificTransport):
            result = await detect_receiver("/dev/ttyUSB2", bauds=[115200, 9600])
        assert result is not None
        assert result.baud == 9600

    @pytest.mark.asyncio
    async def test_no_receiver_found(self):
        mock = _MockTransport(None)
        with patch("lc29h.detect.SerialTransport", return_value=mock):
            result = await detect_receiver("/dev/ttyUSB99", bauds=[115200, 9600])
        assert result is None

    def test_detection_result_fields(self):
        r = DetectionResult(
            port="/dev/ttyUSB2", baud=115200,
            model="LC29HBS", firmware="LC29HBSNR11A01S",
        )
        assert r.port == "/dev/ttyUSB2"
        assert r.baud == 115200
        assert r.model == "LC29HBS"
        assert r.firmware == "LC29HBSNR11A01S"
