"""Embedding backends: Ollama, sentence-transformers, cosine similarity."""
from __future__ import annotations
import json, logging, math, urllib.request
from ._constants import EMBED_MODEL, OLLAMA_ENDPOINT

log = logging.getLogger("pulse.rule_embeddings")
_ST_MODEL = None


def _embed_ollama(texts: list[str], model: str = EMBED_MODEL) -> list[list[float]]:
    url = f"{OLLAMA_ENDPOINT}/api/embed"
    payload = json.dumps({"model": model, "input": texts}).encode()
    req = urllib.request.Request(url, data=payload, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=120) as resp:
        data = json.loads(resp.read().decode())
    embeddings = data.get("embeddings", [])
    if len(embeddings) != len(texts):
        raise ValueError(f"Expected {len(texts)} embeddings, got {len(embeddings)}")
    return embeddings


def _embed_sentence_transformers(texts: list[str]) -> list[list[float]]:
    global _ST_MODEL
    if _ST_MODEL is None:
        import io, os, sys
        os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
        os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
        os.environ.setdefault("HF_HUB_DISABLE_PROGRESS_BARS", "1")
        old_out, old_err = sys.stdout, sys.stderr
        sys.stdout = io.StringIO(); sys.stderr = io.StringIO()
        try:
            from sentence_transformers import SentenceTransformer
            _ST_MODEL = SentenceTransformer("all-MiniLM-L6-v2")
        finally:
            sys.stdout = old_out; sys.stderr = old_err
    return [v.tolist() for v in _ST_MODEL.encode(texts, show_progress_bar=False)]


def embed_texts(texts: list[str]) -> list[list[float]]:
    """Embed texts: sentence-transformers > Ollama fallback."""
    if not texts:
        return []
    try:
        return _embed_sentence_transformers(texts)
    except (ImportError, Exception) as e:
        log.debug("sentence-transformers failed (%s), trying Ollama", e)
    try:
        return _embed_ollama(texts)
    except Exception as e:
        raise RuntimeError(f"No embedding backend available: {e}")


def cosine_similarity(a: list[float], b: list[float]) -> float:
    dot = sum(x * y for x, y in zip(a, b))
    na = sum(x * x for x in a) ** 0.5
    nb = sum(x * x for x in b) ** 0.5
    return 0.0 if na == 0 or nb == 0 else dot / (na * nb)
