import asyncio
from dataclasses import dataclass

import pytest

from kurier import BusType, Station
from kurier.core.basekurier import BaseKurier
from kurier.hermes import Hermes
from kurier.transports.inmemory.bus import InMemoryLine

# Disable Hermes for cleaner test output
Hermes.is_enabled = False


async def _poll(predicate, timeout=2.0, interval=0.05):
    """Poll *predicate* until it returns True or *timeout* seconds elapse."""
    elapsed = 0.0
    while elapsed < timeout:
        if predicate():
            return
        await asyncio.sleep(interval)
        elapsed += interval
    raise AssertionError(f"Condition not met within {timeout}s")


@dataclass
class TestMessage:
    content: str
    value: int


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

    @pytest.mark.asyncio
    async def test_end_to_end_message_flow(self):
        """Test complete message flow from publish to handler."""
        received_messages = []

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

        line = InMemoryLine.get("")

        # Publish a message
        test_message = TestMessage(content="test", value=42)
        await line.publish_async(test_message)

        # Wait for processing
        await _poll(lambda: len(received_messages) >= 1)

        # Verify message was received
        assert len(received_messages) == 1
        assert received_messages[0].content == "test"
        assert received_messages[0].value == 42

        await line.close()

    @pytest.mark.asyncio
    async def test_multiple_message_types(self):
        """Test handling multiple different message types."""
        string_messages = []
        int_messages = []

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

        @Station.add_handler(BusType.IN_MEMORY)
        def int_handler(data: int, *context):
            int_messages.append(data)

        line = InMemoryLine.get("")

        # Publish different types
        await line.publish_async("string message")
        await line.publish_async(123)
        await line.publish_async("another string")

        # Wait for processing
        await _poll(lambda: len(string_messages) >= 2 and len(int_messages) >= 1)

        # Verify correct routing
        assert len(string_messages) == 2
        assert "string message" in string_messages
        assert "another string" in string_messages

        assert len(int_messages) == 1
        assert 123 in int_messages

        await line.close()
