"""Embedding service — encode text and manage vector storage."""

import json
import logging
import time

_logger = logging.getLogger(__name__)

_model_instance = None


def _get_model():
    """Lazy-load the sentence-transformers model (cached across calls)."""
    global _model_instance
    if _model_instance is None:
        try:
            from sentence_transformers import SentenceTransformer
            _model_instance = SentenceTransformer('all-MiniLM-L6-v2')
            _logger.info("Loaded sentence-transformers model: all-MiniLM-L6-v2")
        except ImportError:
            _logger.error(
                "sentence-transformers not installed. "
                "Run: pip install sentence-transformers"
            )
            raise
    return _model_instance


class EmbeddingService:
    """Encode texts, upsert embeddings, and perform semantic search."""

    def __init__(self, env):
        self.env = env
        self.Embedding = env['encoach.embedding'].sudo()

    def encode(self, texts):
        """Batch-encode texts to vectors.

        Args:
            texts: list of strings

        Returns:
            list of float lists (each 384-dim)
        """
        model = _get_model()
        embeddings = model.encode(texts, normalize_embeddings=True, show_progress_bar=False)
        return [e.tolist() for e in embeddings]

    def upsert(self, content_type, content_id, text, metadata=None):
        """Encode and store (or update) a single embedding.

        Returns:
            encoach.embedding record
        """
        if not text or not text.strip():
            return None

        existing = self.Embedding.search([
            ('content_type', '=', content_type),
            ('content_id', '=', content_id),
        ], limit=1)

        vectors = self.encode([text])
        meta_str = json.dumps(metadata or {})

        if existing:
            existing.write({
                'content_text': text[:10000],
                'metadata_json': meta_str,
            })
            existing.set_embedding(vectors[0])
            return existing

        record = self.Embedding.create({
            'content_type': content_type,
            'content_id': content_id,
            'content_text': text[:10000],
            'metadata_json': meta_str,
        })
        record.set_embedding(vectors[0])
        return record

    def search(self, query, *, content_type=None, limit=10):
        """Semantic search — encode query and find similar content.

        Returns:
            list of dicts with text, metadata, similarity score
        """
        if not query or not query.strip():
            return []

        t0 = time.time()
        vectors = self.encode([query])
        results = self.Embedding.similarity_search(
            vectors[0],
            content_type=content_type,
            limit=limit,
        )
        latency = int((time.time() - t0) * 1000)
        _logger.info("Vector search for '%s' returned %d results in %dms",
                      query[:80], len(results), latency)
        return results

    def bulk_index(self, content_type, records_data):
        """Batch-index multiple records.

        Args:
            content_type: embedding content type
            records_data: list of dicts with keys: id, text, metadata
        """
        if not records_data:
            return 0

        texts = [r['text'] for r in records_data if r.get('text')]
        if not texts:
            return 0

        vectors = self.encode(texts)

        indexed = 0
        text_idx = 0
        for r in records_data:
            if not r.get('text'):
                continue
            self.upsert(content_type, r['id'], r['text'], r.get('metadata'))
            text_idx += 1
            indexed += 1

        _logger.info("Bulk-indexed %d %s records", indexed, content_type)
        return indexed

    def delete(self, content_type, content_id):
        """Remove an embedding."""
        existing = self.Embedding.search([
            ('content_type', '=', content_type),
            ('content_id', '=', content_id),
        ])
        if existing:
            existing.unlink()
