"""Tests for runtime/conversation/retrieval.py — offline via stub embedder."""

from __future__ import annotations

import hashlib

import numpy as np
import pytest
from compiler.stages.index import build_index
from runtime.conversation.retrieval import retrieve

DIM = 32


class _HashEmbedder:
    def encode(
        self,
        sentences: list[str],
        *,
        show_progress_bar: bool = False,
        normalize_embeddings: bool = False,
    ) -> np.ndarray:
        vecs = []
        for s in sentences:
            h = hashlib.sha256(s.encode()).digest()
            raw = (h * (DIM // len(h) + 1))[:DIM]
            vec = np.frombuffer(raw, dtype=np.uint8).astype(np.float32) - 128.0
            if normalize_embeddings:
                norm = np.linalg.norm(vec)
                if norm > 0:
                    vec /= norm
            vecs.append(vec)
        return np.stack(vecs)


FAKE = _HashEmbedder()

PASSAGES = [
    "You have power over your mind, not outside events.",
    "The impediment to action advances action.",
    "Confine yourself to the present.",
]


@pytest.fixture
def index_dir(tmp_path):
    idx = tmp_path / "index"
    build_index({"src": PASSAGES}, idx, embedder=FAKE, overwrite=True)
    return idx


def test_retrieve_returns_results(index_dir):
    results = retrieve("mind and power", index_dir, top_k=2, embedder=FAKE)
    assert len(results) > 0
    assert "chunk_id" in results[0]
    assert "source_id" in results[0]
    assert "text" in results[0]


def test_retrieve_empty_index_returns_empty(tmp_path):
    results = retrieve("anything", tmp_path / "index", embedder=FAKE)
    assert results == []


def test_retrieve_top_k_respected(index_dir):
    results = retrieve("action", index_dir, top_k=2, embedder=FAKE)
    assert len(results) <= 2


def test_retrieve_result_has_source_id(index_dir):
    results = retrieve("present", index_dir, top_k=3, embedder=FAKE)
    for r in results:
        assert r["source_id"] == "src"
