"""Abstract base class for all message broker clients.

Analogous to ``db_core.clients.base.DBClient`` — defines the contract that
every backend (Kafka, NATS, RabbitMQ, …) must fulfil.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from ..message.builder import MessageBuilder, Message
    from ..message.result import MessageResult, Subscription


class BrokerClient(ABC):
    """Abstract contract for all message broker clients.

    Concrete backends must implement:

    * :meth:`connect` / :meth:`disconnect`
    * :meth:`topic` — entry point for the fluent :class:`MessageBuilder`
    * :meth:`_publish` — internal publish logic
    * :meth:`_subscribe` — internal subscribe logic
    """

    # ------------------------------------------------------------------
    # Lifecycle (abstract)
    # ------------------------------------------------------------------

    @abstractmethod
    async def connect(self) -> None:
        """Open connections to the broker and prepare resources."""
        ...

    @abstractmethod
    async def disconnect(self) -> None:
        """Close all connections and release resources gracefully."""
        ...

    # ------------------------------------------------------------------
    # Builder entry point (abstract)
    # ------------------------------------------------------------------

    @abstractmethod
    def topic(self, name: str) -> "MessageBuilder":
        """Return a :class:`MessageBuilder` scoped to *name*.

        This is the primary entry point for all operations::

            await client.topic("orders").key("123").payload({...}).publish()
            async with client.topic("orders").subscribe() as sub: ...
        """
        ...

    # ------------------------------------------------------------------
    # Internal operations (abstract — implement per backend)
    # ------------------------------------------------------------------

    @abstractmethod
    async def _publish(self, message: "Message") -> "MessageResult":
        """Publish a single message to the broker.

        Args:
            message: The fully-built :class:`Message` to publish.

        Returns:
            A :class:`MessageResult` with the broker acknowledgment.
        """
        ...

    @abstractmethod
    async def _subscribe(
        self, message: "Message", builder: "MessageBuilder"
    ) -> "Subscription":
        """Start a consumer for the given topic.

        The *builder* parameter carries any per-subscription consumer
        configuration that was set on the fluent chain (e.g. ``group_id``,
        ``auto_offset_reset``).  The backend merges these with its own
        defaults.

        Args:
            message: The :class:`Message` describing the topic.
            builder: The :class:`MessageBuilder` that carried consumer settings.

        Returns:
            A :class:`Subscription` handle.
        """
        ...

    # ------------------------------------------------------------------
    # Convenience methods (built on top of topic() + builder)
    # ------------------------------------------------------------------

    async def publish(
        self,
        topic: str,
        payload: Dict[str, Any],
        key: Optional[str] = None,
        headers: Optional[Dict[str, str]] = None,
        partition: Optional[int] = None,
    ) -> "MessageResult":
        """Quick publish — skips the builder for simple cases.

        Example::

            await client.publish("alerts", {"msg": "error"}, key="alert-1")
        """
        builder = self.topic(topic)
        if key is not None:
            builder.key(key)
        builder.payload(payload)
        if headers:
            builder.headers(headers)
        if partition is not None:
            builder.partition(partition)
        return await builder.publish()

    async def emit(
        self,
        topic: str,
        payload: Dict[str, Any],
        key: Optional[str] = None,
        headers: Optional[Dict[str, str]] = None,
        partition: Optional[int] = None,
    ) -> "MessageResult":
        """Alias for :meth:`publish`."""
        return await self.publish(topic, payload, key=key, headers=headers, partition=partition)

    async def subscribe(
        self,
        topic: str,
        group_id: Optional[str] = None,
        **kwargs: Any,
    ) -> "Subscription":
        """Quick subscribe — returns a :class:`Subscription` async iterator.

        Example::

            async with client.subscribe("orders", group_id="worker") as sub:
                async for msg in sub:
                    print(msg.payload)
        """
        builder = self.topic(topic)
        if group_id is not None:
            builder.group_id(group_id)
        for k, v in kwargs.items():
            if hasattr(builder, k):
                getattr(builder, k)(v)
        return await builder.subscribe()

    async def listen(
        self,
        topic: str,
        callback: Callable,
        group_id: Optional[str] = None,
        **kwargs: Any,
    ) -> "Subscription":
        """Quick listen — callback-based consumer.

        Example::

            sub = await client.listen("orders", handle_order, group_id="worker")
        """
        builder = self.topic(topic)
        if group_id is not None:
            builder.group_id(group_id)
        for k, v in kwargs.items():
            if hasattr(builder, k):
                getattr(builder, k)(v)
        return await builder.listen(callback)

    # ------------------------------------------------------------------
    # Debug helpers
    # ------------------------------------------------------------------

    def raw_message(self, message: "Message") -> str:
        """Return a serialised representation of the message for debugging."""
        return message.debug()

    def debug_message(self, message: "Message") -> str:
        """Alias for :meth:`raw_message`."""
        return message.debug()
