"""Unit tests for embeddings functionality.

Tests get_embeddings method for ControlPlaneManager with CPU engines.
"""

from __future__ import annotations

import pytest

from sagellm_control import ControlPlaneManager


@pytest.fixture
def real_cpu_embedding_engine_model() -> str:
    """Real model id used by embedding tests."""
    pytest.importorskip("sentence_transformers")
    return "sentence-transformers/all-MiniLM-L6-v2"


class TestControlPlaneManagerEmbeddings:
    """Tests for ControlPlaneManager get_embeddings."""

    @pytest.mark.asyncio
    async def test_get_embeddings_no_engine(self):
        """Should raise error when no embedding engines available."""
        manager = ControlPlaneManager()

        texts = ["Test text 1", "Test text 2"]

        # Should raise ValueError when no engines available
        with pytest.raises(ValueError, match="No embedding engines available"):
            await manager.get_embeddings(texts)

    @pytest.mark.asyncio
    async def test_get_embeddings_empty_texts(self):
        """Should return empty list for empty input."""
        manager = ControlPlaneManager()

        embeddings = await manager.get_embeddings([])

        assert embeddings == []

    @pytest.mark.asyncio
    async def test_get_embeddings_with_trace_id(self):
        """Should accept optional trace_id parameter but fail with no engine."""
        manager = ControlPlaneManager()

        # Should raise ValueError when no engines available, even with trace_id
        with pytest.raises(ValueError, match="No embedding engines available"):
            await manager.get_embeddings(
                texts=["Test"],
                model_id="test-model",
                trace_id="custom-trace-123",
            )

    @pytest.mark.asyncio
    async def test_get_embeddings_with_in_process_engine(
        self, real_cpu_embedding_engine_model: str
    ):
        """Should route to real in-process embedding engine when directly registered."""
        pytest.importorskip("sagellm_core")
        try:
            from sagellm_core.engines import CPUEmbeddingEngine
        except ImportError:
            pytest.skip("Installed sagellm-core does not expose CPUEmbeddingEngine yet")

        manager = ControlPlaneManager()
        embedding_engine = CPUEmbeddingEngine(
            model_id=real_cpu_embedding_engine_model, device="cpu"
        )
        await embedding_engine.start()

        try:
            manager.register_engine(
                engine_id=embedding_engine.engine_id,
                model_id=embedding_engine.model_id,
                engine_kind="embedding",
                engine=embedding_engine,
            )

            texts = ["hello", "world!"]
            embeddings = await manager.get_embeddings(
                texts=texts, model_id=embedding_engine.model_id
            )

            assert len(embeddings) == 2
            assert all(len(vector) > 0 for vector in embeddings)
            assert all(isinstance(value, float) for value in embeddings[0])
        finally:
            await embedding_engine.stop()


class TestEmbeddingsPredictability:
    """Tests to ensure CPU engine embeddings are predictable and consistent."""

    @pytest.mark.asyncio
    async def test_cpu_embeddings_placeholder(self):
        """Placeholder for future CPU engine embeddings tests."""
        # TODO: Once CPUEngine supports embeddings, test:
        # - Deterministic embeddings for same input
        # - Different embeddings for different texts
        # - Vector structure and dimensions
        manager = ControlPlaneManager()
        assert manager is not None  # Placeholder
