"""Semantic search over corpus embeddings."""

import json
import os
from pathlib import Path

import numpy as np
import requests

EMBEDDING_MODEL = "openai/text-embedding-3-small"
ENV_PATH = "/home/videobot/dev/.env"


def _load_api_key():
    """Load OpenRouter API key from .env."""
    key = os.environ.get("OPENROUTER_API_KEY")
    if key:
        return key
    with open(ENV_PATH, "r") as f:
        for line in f:
            if line.startswith("OPENROUTER_API_KEY="):
                return line.strip().split("=", 1)[1]
    raise RuntimeError("OPENROUTER_API_KEY not found")


def load_corpus(data_dir: str) -> tuple:
    """Load catalog, embeddings, index from corpus data_dir.

    Returns:
        (catalog: list[dict], embeddings: np.ndarray, index: list[dict])
    """
    data_dir = Path(data_dir)
    with open(data_dir / "catalog.json", "r", encoding="utf-8") as f:
        catalog = json.load(f)
    with open(data_dir / "embeddings_index.json", "r", encoding="utf-8") as f:
        index = json.load(f)
    data = np.load(data_dir / "embeddings.npz")
    embeddings = data["embeddings"]
    return catalog, embeddings, index


def embed_query(api_key: str, text: str) -> np.ndarray:
    """Embed query text via OpenRouter text-embedding-3-small."""
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }
    payload = {
        "model": EMBEDDING_MODEL,
        "input": [text],
    }
    resp = requests.post(
        "https://openrouter.ai/api/v1/embeddings",
        headers=headers,
        json=payload,
        timeout=30,
    )
    if resp.status_code != 200:
        raise RuntimeError(f"Embedding API error {resp.status_code}: {resp.text[:300]}")
    return np.array(resp.json()["data"][0]["embedding"], dtype=np.float32)


def _cosine_similarity(query_vec: np.ndarray, embeddings: np.ndarray) -> np.ndarray:
    """Compute cosine similarity between query and all embeddings."""
    query_norm = query_vec / np.linalg.norm(query_vec)
    norms = np.linalg.norm(embeddings, axis=1, keepdims=True)
    norms = np.where(norms == 0, 1, norms)
    emb_normed = embeddings / norms
    return emb_normed @ query_norm


def search(query: str, data_dir: str, top_n: int = 15,
           images_base: str = None, api_key: str = None) -> list:
    """Search corpus, return ranked results.

    Args:
        query: Search query text
        data_dir: Path to corpus data directory (catalog.json, embeddings.npz, etc.)
        top_n: Number of results to return
        images_base: Base directory for resolving relative image paths in catalog.
                     Full path = images_base / entry["image_path"]
        api_key: OpenRouter API key (auto-loaded from env if None)

    Returns:
        List of dicts: [{"rank": 1, "score": 0.85, "id": "...", "title": "...",
                         "description": "...", "image_path": "/abs/path/to/img.jpg",
                         "year": "...", "source": "..."}, ...]
    """
    if api_key is None:
        api_key = _load_api_key()

    catalog, embeddings, index = load_corpus(data_dir)

    query_vec = embed_query(api_key, query)
    scores = _cosine_similarity(query_vec, embeddings)
    top_indices = np.argsort(scores)[::-1][:top_n]

    results = []
    for rank, idx in enumerate(top_indices):
        entry_info = index[idx]
        cat_entry = catalog[entry_info["catalog_idx"]]

        image_path = cat_entry.get("image_path", "")
        if images_base and image_path:
            image_path = str(Path(images_base) / image_path)

        results.append({
            "rank": rank + 1,
            "score": float(scores[idx]),
            "id": cat_entry.get("id", ""),
            "title": cat_entry.get("title") or "(no title)",
            "description": cat_entry.get("description", ""),
            "year": cat_entry.get("year") or "?",
            "source": cat_entry.get("source", ""),
            "image_path": image_path,
        })

    return results
