import asyncio

import pytest

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

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")


class TestInMemoryLine:
    def setup_method(self):
        BaseKurier._registry.clear()
        InMemoryLine._registry.clear()

    @pytest.fixture
    def line(self):
        """Create a fresh InMemoryLine instance for each test."""
        line = InMemoryLine()
        yield line
        # Cleanup
        asyncio.run(line.close())

    def test_line_initialization(self, line):
        """Test that the line initializes correctly."""
        assert line.name == "in_memory_instance_line"
        assert line._running is True
        assert line._consumer_thread.is_alive()

    @pytest.mark.asyncio
    async def test_publish_async(self, line):
        """Test async publishing a simple message."""
        test_message = "test_message"
        await line.publish_async(test_message)
        assert line._message_queue.qsize() > 0

    @pytest.mark.asyncio
    async def test_publish_async_with_metadata(self, line):
        """Test async publishing with metadata."""
        test_message = "test_message"
        metadata = {"source": "test", "priority": 1}

        await line.publish_async(test_message, metadata)
        assert line._message_queue.qsize() > 0

    def test_publish(self, line):
        """Test synchronous publish."""
        line._running = False
        line._consumer_thread.join(timeout=2)
        line.publish("sync_message")
        assert line._message_queue.qsize() > 0

    def test_publish_with_metadata(self, line):
        """Test synchronous publish with metadata."""
        line._running = False
        line._consumer_thread.join(timeout=2)
        line.publish("sync_message", {"source": "test"})
        assert line._message_queue.qsize() > 0

    def test_publish_batch(self, line):
        """Test synchronous batch publish."""
        line._running = False
        line._consumer_thread.join(timeout=2)
        messages = ["msg1", "msg2", "msg3"]
        line.publish_batch(messages, {}, batch_size=2)
        assert line._message_queue.qsize() > 0

    @pytest.mark.asyncio
    async def test_line_close(self, line):
        """Test graceful shutdown of the line."""
        assert line._running is True
        assert line._consumer_thread.is_alive()

        await line.close()

        assert line._running is False
        await _poll(lambda: not line._consumer_thread.is_alive())
