"""Abstract base class for all LLM/VLM backends."""

from __future__ import annotations

from abc import ABC, abstractmethod
from collections.abc import AsyncIterator
from typing import TYPE_CHECKING, Any, Self

if TYPE_CHECKING:
    from thryve.core.context.models import BackendInfo, ChatResponse, Message
    from thryve.core.tools.models import Tool


class BaseBackend(ABC):
    """Every concrete backend must implement this interface.

    The design follows DESIGN.md Section 3.1 – generate (sync), generate_async,
    generate_stream, embeddings, bind_tools, get_info.
    """

    # ------------------------------------------------------------------
    # Generation
    # ------------------------------------------------------------------

    @abstractmethod
    def generate(self, messages: list[Message], **kwargs: Any) -> ChatResponse:
        """Synchronous chat completion (supports multimodal messages)."""

    @abstractmethod
    async def generate_async(self, messages: list[Message], **kwargs: Any) -> ChatResponse:
        """Asynchronous chat completion (supports multimodal messages)."""

    @abstractmethod
    async def generate_stream(self, messages: list[Message], **kwargs: Any) -> AsyncIterator[str]:
        """Asynchronous streaming chat completion.

        Yields text chunks as they arrive.  Tool-call responses are typically
        *not* streamed – callers should fall back to ``generate_async`` when
        tools are bound and the model triggers a function call.
        """
        # Provide default so subclasses can ``yield`` without error.
        yield ""  # pragma: no cover

    # ------------------------------------------------------------------
    # Embeddings
    # ------------------------------------------------------------------

    @abstractmethod
    def embeddings(self, texts: list[str], **kwargs: Any) -> list[list[float]]:
        """Compute embedding vectors for *texts*."""

    # ------------------------------------------------------------------
    # Tool binding
    # ------------------------------------------------------------------

    @abstractmethod
    def bind_tools(self, tools: list[Tool]) -> Self:
        """Return a *new* backend instance with *tools* bound for function calling.

        The original instance is left unchanged (immutable pattern).
        """

    # ------------------------------------------------------------------
    # Introspection
    # ------------------------------------------------------------------

    @abstractmethod
    def get_info(self) -> BackendInfo:
        """Return metadata about this backend (provider, model, capabilities)."""
