from abc import ABC, abstractmethod
from typing import Any, List, Sequence

from common.models.common import ChatMessage


class ChatMessageDao(ABC):
    """
    Storage-agnostic contract for chat-message persistence/querying.
    Concrete implementations must fulfil the methods declared here.
    """

    # ------------------------------------------------------------------ #
    # CRUD-like operations                                               #
    # ------------------------------------------------------------------ #

    @abstractmethod
    async def save_message(self, message: ChatMessage) -> str | None:
        """Persist a single chat message."""
        raise NotImplementedError

    @abstractmethod
    async def delete_message(self, message_id: int | str) -> bool | None:
        """Remove an already-stored message."""
        raise NotImplementedError

    @abstractmethod
    async def get_message_by_id(self, message_id: int | str) -> ChatMessage | None:
        """Fetch a message by its primary identifier."""
        raise NotImplementedError

    # ------------------------------------------------------------------ #
    # Query helpers                                                      #
    # ------------------------------------------------------------------ #

    @abstractmethod
    async def get_messages_by_time_range(
        self, broadcaster_user_id: str, start_timestamp: float, end_timestamp: float
    ) -> Sequence[ChatMessage]:
        """All messages whose timestamp is between `start` and `end`."""
        raise NotImplementedError

    @abstractmethod
    async def get_messages_by_broadcaster(
        self,
        broadcaster_id: int | str,
        limit: int | None = None,
    ) -> Sequence[ChatMessage]:
        """Messages that belong to the given broadcaster."""
        raise NotImplementedError

    @abstractmethod
    async def get_messages_by_chatter(
        self,
        chatter_id: int | str,
        limit: int | None = None,
    ) -> Sequence[ChatMessage]:
        """Messages posted by a particular user/chatter."""
        raise NotImplementedError

    @abstractmethod
    async def get_messages_by_stream(
        self,
        stream_id: int | str,
        limit: int | None = None,
    ) -> Sequence[ChatMessage]:
        """Messages associated with a specific stream."""
        raise NotImplementedError

    @abstractmethod
    async def count_messages_by_stream(self, stream_id: int | str) -> int:
        """Number of messages posted during a particular stream."""
        raise NotImplementedError

    # ------------------------------------------------------------------ #
    # Internal / factory helpers                                         #
    # ------------------------------------------------------------------ #

    @abstractmethod
    def _build_messages_from_rows(
        self,
        rows: List[tuple[Any, ...]],
    ) -> List[ChatMessage]:
        """Convert raw DB rows into domain objects."""
        raise NotImplementedError

    @abstractmethod
    def _build_message_from_data(
        self,
        data: dict[str, Any] | tuple[Any, ...],
    ) -> ChatMessage:
        """Create a domain object from a raw mapping/dict."""
        raise NotImplementedError

    @abstractmethod
    def _to_jsonb(self, payload: Any) -> Any:
        """
        Convert a Python mapping to a driver-specific JSONB representation.
        For non-SQL stores this can simply return the original dict.
        """
        raise NotImplementedError
