"""Integration tests for KafkaClient.

These tests require a running Kafka broker.  Set ``KAFKA_BOOTSTRAP_SERVERS``
or default to ``localhost:9092``.

Run with::

    pytest tests/test_kafka.py -v -m kafka
"""

import asyncio
import pytest
from broker_core import KafkaConfig, MessageResult, ReceivedMessage


pytestmark = pytest.mark.kafka


class TestKafkaConnect:
    @pytest.mark.asyncio
    async def test_connect_and_disconnect(self, kafka_client):
        """Client can connect and disconnect without errors."""
        # Already connected via fixture
        assert kafka_client._connected is True

    @pytest.mark.asyncio
    async def test_connect_is_idempotent(self, kafka_client):
        """Calling connect() on an already-connected client is safe."""
        await kafka_client.connect()
        assert kafka_client._connected is True


class TestKafkaPublish:
    @pytest.mark.asyncio
    async def test_publish_single_message(self, kafka_client, test_topic):
        """Publishing a message returns a valid MessageResult."""
        result = await kafka_client.topic(test_topic) \
            .key("test-key") \
            .payload({"message": "hello", "count": 1}) \
            .headers({"source": "test"}) \
            .publish()

        assert isinstance(result, MessageResult)
        assert result.topic == test_topic
        assert result.success is True
        assert result.partition >= 0
        assert result.offset >= 0

    @pytest.mark.asyncio
    async def test_publish_convenience_method(self, kafka_client, test_topic):
        """The convenience publish() works without the builder."""
        result = await kafka_client.publish(
            test_topic, {"msg": "quick"}, key="k1", headers={"h": "v"}
        )
        assert result.success is True
        assert result.topic == test_topic

    @pytest.mark.asyncio
    async def test_emit_is_alias_for_publish(self, kafka_client, test_topic):
        """emit() is an alias for publish()."""
        result = await kafka_client.topic(test_topic) \
            .payload({"msg": "emit test"}) \
            .emit()
        assert result.success is True

    @pytest.mark.asyncio
    async def test_publish_without_key(self, kafka_client, test_topic):
        """Publishing without a key still succeeds (round-robin partitioning)."""
        result = await kafka_client.topic(test_topic) \
            .payload({"msg": "no key"}) \
            .publish()
        assert result.success is True

    @pytest.mark.asyncio
    async def test_publish_not_connected_raises(self):
        """Publishing on a disconnected client raises RuntimeError."""
        config = KafkaConfig(bootstrap_servers="localhost:9094")
        from broker_core import KafkaClient
        client = KafkaClient(config)
        with pytest.raises(RuntimeError, match="not connected"):
            await client.topic("test").payload({}).publish()


class TestKafkaSubscribe:
    @pytest.mark.asyncio
    async def test_subscribe_and_consume(self, kafka_client, test_topic):
        """Publish a message, then consume it via subscribe()."""
        # Publish first
        await kafka_client.topic(test_topic) \
            .key("sub-test") \
            .payload({"order_id": 42}) \
            .publish()

        # Small delay for propagation
        await asyncio.sleep(0.5)

        # Subscribe
        received = []
        sub = await kafka_client.topic(test_topic) \
            .group_id(f"test-group-{test_topic}") \
            .auto_offset_reset("earliest") \
            .subscribe()
        async with sub:
            try:
                async for msg in sub:
                    received.append(msg)
                    await msg.ack()
                    break
            except asyncio.TimeoutError:
                pass

        assert len(received) >= 1
        msg = received[0]
        assert isinstance(msg, ReceivedMessage)
        assert msg.payload.get("order_id") == 42

    @pytest.mark.asyncio
    async def test_listen_callback(self, kafka_client, test_topic):
        """Messages are delivered to a callback via listen()."""
        await kafka_client.topic(test_topic) \
            .payload({"event": "login"}) \
            .publish()

        await asyncio.sleep(0.5)

        received_events = []

        async def handle(msg: ReceivedMessage):
            received_events.append(msg.payload)
            await msg.ack()

        sub = await kafka_client.topic(test_topic) \
            .group_id(f"cb-group-{test_topic}") \
            .auto_offset_reset("earliest") \
            .listen(handle)

        # Wait a bit for message delivery
        await asyncio.sleep(1.0)
        await sub.stop()

        assert len(received_events) >= 1
        assert received_events[0].get("event") == "login"

    @pytest.mark.asyncio
    async def test_convenience_subscribe(self, kafka_client, test_topic):
        """The convenience subscribe() works."""
        await kafka_client.topic(test_topic).payload({"x": 1}).publish()
        await asyncio.sleep(0.5)

        sub = await kafka_client.subscribe(
            test_topic,
            group_id=f"conv-group-{test_topic}",
            auto_offset_reset="earliest",
        )
        async with sub:
            async for msg in sub:
                assert msg.payload["x"] == 1
                await msg.ack()
                break

    @pytest.mark.asyncio
    async def test_convenience_listen(self, kafka_client, test_topic):
        """The convenience listen() works."""
        await kafka_client.topic(test_topic).payload({"y": 2}).publish()
        await asyncio.sleep(0.5)

        events = []

        async def handler(msg: ReceivedMessage):
            events.append(msg.payload)
            await msg.ack()

        sub = await kafka_client.listen(
            test_topic, handler,
            group_id=f"conv-listen-{test_topic}",
            auto_offset_reset="earliest",
        )
        await asyncio.sleep(1.0)
        await sub.stop()

        assert len(events) >= 1
        assert events[0].get("y") == 2
