#!/usr/bin/env python3
"""rag_hybrid.py — Hybrid search: FTS5 + cosine similarity"""
import sqlite3, numpy as np, math, os
from numpy.linalg import norm

DB_PATH = os.path.expanduser("~/.cerebro/rag_conocimiento.db")

def blob_to_vector(blob: bytes) -> np.ndarray:
    return np.frombuffer(blob, dtype=np.float32).copy()

def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
    return float(np.dot(a, b))

def get_embedding_ollama(text: str) -> np.ndarray:
    import requests
    resp = requests.post("http://localhost:11434/api/embeddings",
        json={"model": "bge-m3", "input": text})
    return np.array(resp.json()["embeddings"][0], dtype=np.float32)

def hybrid_search(query: str, alpha: float = 0.4, top_k: int = 10):
    """
    alpha: peso para FTS5, (1-alpha) para cosine similarity.
    0.6 = nombres propios, 0.2 = conceptual, 0.4 = equilibrio
    """
    conn = sqlite3.connect(DB_PATH)
    query_emb = get_embedding_ollama(query)
    query_emb = query_emb / (norm(query_emb) + 1e-10)
    
    cursor = conn.execute("""
        SELECT c.id, c.contenido, c.embedding,
               bm25(chunks_fts, 0.0, 0.0, 5.0, 5.0) as fts_score
        FROM chunks_fts
        JOIN chunks c ON chunks_fts.rowid = c.id
        WHERE chunks_fts MATCH ?
        ORDER BY fts_score DESC
        LIMIT ?
    """, (query, top_k * 2))
    
    results = []
    for row in cursor:
        chunk_id, contenido, emb_blob, fts_score = row
        fts_norm = 1.0 / (1.0 + math.exp(-fts_score / 10.0))
        emb_vec = blob_to_vector(emb_blob)
        emb_vec = emb_vec / (norm(emb_vec) + 1e-10)
        cos_sim = cosine_similarity(query_emb, emb_vec)
        score_total = alpha * fts_norm + (1.0 - alpha) * cos_sim
        results.append((score_total, contenido[:200], fts_norm, cos_sim))
    
    results.sort(key=lambda x: x[0], reverse=True)
    conn.close()
    return results[:top_k]