import pytest

from kurier.core.basekurier import BaseKurier
from kurier.core.bus_types import BusType
from kurier.core.kurier_message import KurierMessage
from kurier.core.station import Station
from kurier.hermes import Hermes
from kurier.transports.inmemory.bus import InMemoryLine

Hermes.is_enabled = False


class TestStationDispatch:
    def setup_method(self):
        """Clear the registry before each test."""
        Station.station_registry.clear()
        BaseKurier._registry.clear()
        InMemoryLine._registry.clear()

    @pytest.fixture
    def line(self):
        """Create a fresh InMemoryLine instance for dispatch tests."""
        line = InMemoryLine()
        yield line
        import asyncio

        asyncio.run(line.close())

    @pytest.mark.asyncio
    async def test_dispatch_to_sync_handler(self, line):
        """Test dispatching to synchronous handlers."""
        received_messages = []

        @Station.add_handler(BusType.IN_MEMORY)
        def handler(data: str, *context):
            received_messages.append(data)

        # Create test envelope
        envelope = KurierMessage(
            object_type_name="str",
            message_payload='"test_message"',
            metadata={},
        )

        result = await Station.dispatch(line, envelope.model_dump_json())

        assert result is True
        assert len(received_messages) == 1
        assert received_messages[0] == "test_message"

    @pytest.mark.asyncio
    async def test_dispatch_to_async_handler(self, line):
        """Test dispatching to asynchronous handlers."""
        received_messages = []

        @Station.add_handler(BusType.IN_MEMORY)
        async def handler(data: str, *context):
            received_messages.append(data)

        envelope = KurierMessage(object_type_name="str", message_payload='"async_test"', metadata={})

        result = await Station.dispatch(line, envelope.model_dump_json())

        assert result is True
        assert len(received_messages) == 1
        assert received_messages[0] == "async_test"

    @pytest.mark.asyncio
    async def test_dispatch_multiple_handlers(self, line):
        """Test dispatching to multiple handlers for the same message type."""
        received_messages = []

        @Station.add_handler(BusType.IN_MEMORY)
        def handler1(data: str, *context):
            received_messages.append(f"handler1: {data}")

        @Station.add_handler(BusType.IN_MEMORY)
        def handler2(data: str, *context):
            received_messages.append(f"handler2: {data}")

        envelope = KurierMessage(object_type_name="str", message_payload='"multi_test"', metadata={})

        result = await Station.dispatch(line, envelope.model_dump_json())

        assert result is True
        assert len(received_messages) == 2
        assert "handler1: multi_test" in received_messages
        assert "handler2: multi_test" in received_messages

    @pytest.mark.asyncio
    async def test_dispatch_unknown_message_type(self, line):
        """Test dispatching unknown message types."""

        @Station.add_handler(BusType.IN_MEMORY)
        def handler(data: str, *context):
            pass

        envelope = KurierMessage(
            object_type_name="UnknownType",
            message_payload='"test"',
            metadata={},
        )

        result = await Station.dispatch(line, envelope.model_dump_json())

        assert result is False

    @pytest.mark.asyncio
    async def test_dispatch_malformed_envelope(self, line):
        """Test dispatching with malformed envelope."""

        @Station.add_handler(BusType.IN_MEMORY)
        def handler(data: str, *context):
            pass

        malformed_json = '{"invalid": "envelope"}'
        result = await Station.dispatch(line, malformed_json)

        assert result is False

    @pytest.mark.asyncio
    async def test_dispatch_handler_exception(self, line):
        """Test that exceptions in message handlers propagate to the caller."""

        @Station.add_handler(BusType.IN_MEMORY)
        def failing_handler(data: str, *context):
            raise Exception("Handler failed")

        envelope = KurierMessage(object_type_name="str", message_payload='"test"', metadata={})

        with pytest.raises(Exception, match="Handler failed"):
            await Station.dispatch(line, envelope.model_dump_json())
