"""
Local AI Service - Unified interface for all local AI models.
100% free, runs on CPU. No API costs.

Models:
- LLM: Ollama + Qwen 2.5 3B (text generation, analysis)
- Sentiment: VADER (instant, great for social media)
- Embeddings: sentence-transformers (semantic search)
"""

import logging
import time

import httpx

logger = logging.getLogger(__name__)

OLLAMA_URL = "http://localhost:11434"
DEFAULT_MODEL = "qwen2.5:3b"


# ── LLM (Ollama) ──────────────────────────────────────────────

async def llm_chat(prompt: str, system: str = "", model: str = DEFAULT_MODEL,
                   max_tokens: int = 1024, temperature: float = 0.3) -> str:
    """Generate text with local LLM via Ollama."""
    messages = []
    if system:
        messages.append({"role": "system", "content": system})
    messages.append({"role": "user", "content": prompt})

    try:
        async with httpx.AsyncClient(timeout=120) as client:
            r = await client.post(
                f"{OLLAMA_URL}/v1/chat/completions",
                json={
                    "model": model,
                    "messages": messages,
                    "max_tokens": max_tokens,
                    "temperature": temperature,
                },
            )
            r.raise_for_status()
            return r.json()["choices"][0]["message"]["content"]
    except Exception as e:
        logger.error(f"Ollama LLM error: {e}")
        return ""


def llm_chat_sync(prompt: str, system: str = "", model: str = DEFAULT_MODEL,
                  max_tokens: int = 1024, temperature: float = 0.3) -> str:
    """Sync version of llm_chat."""
    import httpx as httpx_sync

    messages = []
    if system:
        messages.append({"role": "system", "content": system})
    messages.append({"role": "user", "content": prompt})

    try:
        with httpx_sync.Client(timeout=120) as client:
            r = client.post(
                f"{OLLAMA_URL}/v1/chat/completions",
                json={
                    "model": model,
                    "messages": messages,
                    "max_tokens": max_tokens,
                    "temperature": temperature,
                },
            )
            r.raise_for_status()
            return r.json()["choices"][0]["message"]["content"]
    except Exception as e:
        logger.error(f"Ollama LLM sync error: {e}")
        return ""


async def llm_available() -> bool:
    """Check if Ollama is running and model is available."""
    try:
        async with httpx.AsyncClient(timeout=5) as client:
            r = await client.get(f"{OLLAMA_URL}/api/tags")
            if r.status_code == 200:
                models = [m["name"] for m in r.json().get("models", [])]
                return any(DEFAULT_MODEL.split(":")[0] in m for m in models)
        return False
    except Exception:
        return False


# ── Sentiment Analysis (VADER) ─────────────────────────────────

_vader = None


def _get_vader():
    global _vader
    if _vader is None:
        from nltk.sentiment import SentimentIntensityAnalyzer
        _vader = SentimentIntensityAnalyzer()
    return _vader


def analyze_sentiment(text: str) -> dict:
    """Analyze sentiment of text using VADER. Instant, no GPU needed."""
    if not text or not text.strip():
        return {"label": "neutro", "score": 0.0, "compound": 0.0}

    sia = _get_vader()
    scores = sia.polarity_scores(text)
    compound = scores["compound"]

    if compound >= 0.05:
        label = "positivo"
    elif compound <= -0.05:
        label = "negativo"
    else:
        label = "neutro"

    return {
        "label": label,
        "score": compound,
        "compound": compound,
        "positive": scores["pos"],
        "negative": scores["neg"],
        "neutral": scores["neu"],
    }


def analyze_sentiment_batch(texts: list[str]) -> dict:
    """Analyze sentiment of multiple texts. Returns aggregate + individual."""
    if not texts:
        return {"average": 0.0, "distribution": {"positivo": 0, "negativo": 0, "neutro": 0}, "results": []}

    results = [analyze_sentiment(t) for t in texts]
    distribution = {"positivo": 0, "negativo": 0, "neutro": 0}
    total_compound = 0.0

    for r in results:
        distribution[r["label"]] += 1
        total_compound += r["compound"]

    avg = total_compound / len(results) if results else 0.0

    return {
        "average": round(avg, 3),
        "distribution": distribution,
        "total": len(results),
        "results": results,
    }


# ── Embeddings (sentence-transformers) ─────────────────────────

_embed_model = None


def _get_embed_model():
    global _embed_model
    if _embed_model is None:
        from sentence_transformers import SentenceTransformer
        logger.info("Loading embedding model...")
        t0 = time.time()
        _embed_model = SentenceTransformer("all-MiniLM-L6-v2")
        logger.info(f"Embedding model loaded in {time.time() - t0:.1f}s")
    return _embed_model


def get_embeddings(texts: list[str]) -> list:
    """Get embeddings for texts. Returns list of float vectors."""
    model = _get_embed_model()
    embeddings = model.encode(texts, show_progress_bar=False)
    return embeddings.tolist()


def semantic_similarity(text_a: str, text_b: str) -> float:
    """Calculate cosine similarity between two texts. 0-1 scale."""
    from sentence_transformers.util import cos_sim
    model = _get_embed_model()
    emb_a = model.encode([text_a])
    emb_b = model.encode([text_b])
    sim = cos_sim(emb_a, emb_b)[0][0].item()
    return round(sim, 4)
