"""Tests for serial transport watcher mechanism."""

import asyncio

import pytest

from lc29h.transport import SerialTransport


class TestResponseWatcher:
    """Test the dispatch_line / watcher mechanism without real serial hardware."""

    def test_dispatch_no_watchers(self):
        """dispatch_line returns False when no watchers registered."""
        transport = SerialTransport()
        assert transport.dispatch_line("$GPGGA,whatever") is False

    def test_dispatch_matching_watcher(self):
        """dispatch_line resolves matching watcher's future."""
        transport = SerialTransport()
        loop = asyncio.new_event_loop()
        future = loop.create_future()
        from lc29h.transport import _ResponseWatcher

        watcher = _ResponseWatcher(prefix="$PAIR021", future=future)
        transport._watchers.append(watcher)

        result = transport.dispatch_line("$PAIR021,LC29HDANR*13")
        assert result is True
        assert future.done()
        assert future.result() == "$PAIR021,LC29HDANR*13"
        assert watcher not in transport._watchers
        loop.close()

    def test_dispatch_non_matching(self):
        """dispatch_line returns False for non-matching lines."""
        transport = SerialTransport()
        loop = asyncio.new_event_loop()
        future = loop.create_future()
        from lc29h.transport import _ResponseWatcher

        watcher = _ResponseWatcher(prefix="$PAIR021", future=future)
        transport._watchers.append(watcher)

        result = transport.dispatch_line("$GPGGA,whatever")
        assert result is False
        assert not future.done()
        assert watcher in transport._watchers
        loop.close()

    def test_dispatch_multiple_watchers(self):
        """First matching watcher is resolved, others stay pending."""
        transport = SerialTransport()
        loop = asyncio.new_event_loop()

        future1 = loop.create_future()
        future2 = loop.create_future()
        from lc29h.transport import _ResponseWatcher

        w1 = _ResponseWatcher(prefix="$PAIR001", future=future1)
        w2 = _ResponseWatcher(prefix="$PQTMVERNO", future=future2)
        transport._watchers.extend([w1, w2])

        transport.dispatch_line("$PQTMVERNO,FW1.0*XX")
        assert not future1.done()
        assert future2.done()
        assert w1 in transport._watchers
        assert w2 not in transport._watchers
        loop.close()

    @pytest.mark.asyncio
    async def test_wait_for_prefix_timeout(self):
        """_wait_for_prefix returns None on timeout."""
        transport = SerialTransport()
        transport._monitor_active = True
        result = await transport._wait_for_prefix("$PAIR021", timeout=0.05)
        assert result is None

    @pytest.mark.asyncio
    async def test_wait_for_prefix_resolved(self):
        """_wait_for_prefix returns line when watcher is resolved."""
        transport = SerialTransport()
        transport._monitor_active = True

        async def _resolve_after_delay():
            await asyncio.sleep(0.01)
            transport.dispatch_line("$PAIR021,LC29HDANR*13")

        task = asyncio.create_task(_resolve_after_delay())
        result = await transport._wait_for_prefix("$PAIR021", timeout=1.0)
        await task
        assert result == "$PAIR021,LC29HDANR*13"
