"""GenAI-backed embeddings for semantic search."""

import io
import logging
from typing import TYPE_CHECKING

import numpy as np
from PIL import Image

if TYPE_CHECKING:
    from frigate.genai import GenAIClient

logger = logging.getLogger(__name__)

EMBEDDING_DIM = 768


class GenAIEmbedding:
    """Embedding adapter that delegates to a GenAI provider's embed API.

    Provides the same interface as JinaV2Embedding for semantic search:
    __call__(inputs, embedding_type) -> list[np.ndarray]. Output embeddings are
    normalized to 768 dimensions for Frigate's sqlite-vec schema.
    """

    def __init__(self, client: "GenAIClient") -> None:
        self.client = client

    def __call__(
        self,
        inputs: list[str] | list[bytes] | list[Image.Image],
        embedding_type: str = "text",
    ) -> list[np.ndarray]:
        """Generate embeddings for text or images.

        Args:
            inputs: List of strings (text) or bytes/PIL images (vision).
            embedding_type: "text" or "vision".

        Returns:
            List of 768-dim numpy float32 arrays.
        """
        if not inputs:
            return []

        if embedding_type == "text":
            texts = [str(x) for x in inputs]
            embeddings = self.client.embed(texts=texts)
        elif embedding_type == "vision":
            images: list[bytes] = []
            for inp in inputs:
                if isinstance(inp, bytes):
                    images.append(inp)
                elif isinstance(inp, Image.Image):
                    buf = io.BytesIO()
                    inp.convert("RGB").save(buf, format="JPEG")
                    images.append(buf.getvalue())
                else:
                    logger.warning(
                        "GenAIEmbedding: skipping unsupported vision input type %s",
                        type(inp).__name__,
                    )
            if not images:
                return []
            embeddings = self.client.embed(images=images)
        else:
            raise ValueError(
                f"Invalid embedding_type '{embedding_type}'. Must be 'text' or 'vision'."
            )

        result = []
        for emb in embeddings:
            arr = np.asarray(emb, dtype=np.float32)
            if arr.ndim > 1:
                # Some providers return token-level embeddings; pool to one vector.
                arr = arr.mean(axis=0)
            arr = arr.flatten()
            if arr.size != EMBEDDING_DIM:
                if arr.size > EMBEDDING_DIM:
                    arr = arr[:EMBEDDING_DIM]
                else:
                    arr = np.pad(
                        arr,
                        (0, EMBEDDING_DIM - arr.size),
                        mode="constant",
                        constant_values=0,
                    )
            result.append(arr)
        return result
