"""Local embedder — sentence-transformers for fast, local embedding."""

from __future__ import annotations

import logging
import time
from functools import lru_cache
from typing import TYPE_CHECKING

import numpy as np

if TYPE_CHECKING:
    from sentence_transformers import SentenceTransformer

logger = logging.getLogger("ecate.router.embedder")

# Default multilingual model (512 dimensions, supports 50+ languages)
DEFAULT_MODEL = "sentence-transformers/distiluse-base-multilingual-cased-v1"


class LocalEmbedder:
    """Fast local text embedder using sentence-transformers.
    
    Uses distiluse-base-multilingual-cased-v1 by default:
    - 512 dimensions
    - Supports 50+ languages including English and Italian
    - ~10ms per embedding on CPU
    """
    
    def __init__(self, model_name: str = DEFAULT_MODEL):
        self._model_name = model_name
        self._model: SentenceTransformer | None = None
        self._dim: int | None = None
    
    def _load_model(self) -> "SentenceTransformer":
        """Lazy-load the model on first use."""
        if self._model is None:
            try:
                from sentence_transformers import SentenceTransformer
            except ImportError:
                raise ImportError(
                    "sentence-transformers is required for embedding-based routing. "
                    "Install with: pip install sentence-transformers"
                )
            
            logger.info(f"Loading embedder: {self._model_name}")
            start = time.monotonic()
            self._model = SentenceTransformer(self._model_name)
            self._dim = self._model.get_sentence_embedding_dimension()
            elapsed = (time.monotonic() - start) * 1000
            logger.info(f"Embedder loaded: {self._dim}d, {elapsed:.0f}ms")
        
        return self._model
    
    @property
    def dim(self) -> int:
        """Embedding dimension."""
        if self._dim is None:
            self._load_model()
        return self._dim  # type: ignore
    
    def embed(self, text: str) -> np.ndarray:
        """Embed a single text.
        
        Returns:
            np.ndarray of shape (dim,)
        """
        model = self._load_model()
        return model.encode(text, convert_to_numpy=True, normalize_embeddings=True)
    
    def embed_batch(self, texts: list[str], show_progress: bool = False) -> np.ndarray:
        """Embed multiple texts.
        
        Returns:
            np.ndarray of shape (n_texts, dim)
        """
        model = self._load_model()
        return model.encode(
            texts, 
            convert_to_numpy=True, 
            normalize_embeddings=True,
            show_progress_bar=show_progress,
        )


# Singleton instance for reuse
_embedder: LocalEmbedder | None = None


def get_embedder(model_name: str = DEFAULT_MODEL) -> LocalEmbedder:
    """Get or create the global embedder instance."""
    global _embedder
    if _embedder is None or _embedder._model_name != model_name:
        _embedder = LocalEmbedder(model_name)
    return _embedder


@lru_cache(maxsize=1024)
def embed_cached(text: str) -> tuple[float, ...]:
    """Embed text with caching (returns tuple for hashability).
    
    Use for frequently repeated prompts (e.g., system messages).
    """
    embedder = get_embedder()
    vec = embedder.embed(text)
    return tuple(vec.tolist())
