"""Semantic product search with embeddings (lesson 10)."""

import numpy as np

EMBED_MODEL = "text-embedding-3-small"


class ProductSearch:
    def __init__(self, client, db, batch_size: int = 100) -> None:
        rows = db.all_products()
        self.products = rows
        texts = [f"{name} ({cat} / {sub or 'n/a'})" for _, name, cat, sub in rows]
        vectors = []
        for i in range(0, len(texts), batch_size):                 # batch: one request per 100 texts
            resp = client.embeddings.create(model=EMBED_MODEL, input=texts[i:i + batch_size])
            vectors += [d.embedding for d in resp.data]
        self.matrix = np.array(vectors, dtype=np.float32)
        self.matrix /= np.linalg.norm(self.matrix, axis=1, keepdims=True)
        self.client = client

    def search(self, text: str, k: int = 5) -> list[dict]:
        k = max(1, min(int(k), 20))
        q = np.array(self.client.embeddings.create(model=EMBED_MODEL, input=text).data[0].embedding,
                     dtype=np.float32)
        q /= np.linalg.norm(q)
        scores = self.matrix @ q
        best = np.argsort(-scores)[:k]
        return [{"product_id": self.products[i][0], "product_name": self.products[i][1],
                 "category": self.products[i][2], "score": round(float(scores[i]), 3)} for i in best]
