"""
Tests for the multi-backend embedding client.
"""

from unittest.mock import MagicMock, patch

from django.test import TestCase

from library.services.embedding_client import EmbeddingClient


class MockLLMModel:
    """Mock LLMModel for testing."""

    def __init__(self, api_type="openai", supports_multimodal=False, vector_dimensions=None):
        self.name = "test-embedding-model"
        self.supports_multimodal = supports_multimodal
        self.vector_dimensions = vector_dimensions
        self.input_cost_per_1k = "0.0001"
        self.api = MockLLMApi(api_type=api_type)


class MockLLMApi:
    """Mock LLMApi for testing."""

    def __init__(self, api_type="openai"):
        self.name = "Test API"
        self.api_type = api_type
        self.base_url = "http://localhost:8080/v1"
        self.api_key = "test-key"
        self.timeout_seconds = 60


class EmbeddingClientInitTests(TestCase):
    """Tests for EmbeddingClient initialization."""

    def test_init_openai(self):
        model = MockLLMModel(api_type="openai")
        client = EmbeddingClient(model)
        self.assertEqual(client.api_type, "openai")
        self.assertEqual(client.model_name, "test-embedding-model")

    def test_init_bedrock(self):
        model = MockLLMModel(api_type="bedrock")
        model.api.base_url = "https://bedrock-runtime.us-east-1.amazonaws.com"
        client = EmbeddingClient(model)
        self.assertEqual(client.api_type, "bedrock")

    def test_init_with_user(self):
        model = MockLLMModel()
        user = MagicMock()
        client = EmbeddingClient(model, user=user)
        self.assertEqual(client.user, user)


class OpenAIResponseParsingTests(TestCase):
    """Tests for OpenAI-compatible response parsing."""

    def setUp(self):
        self.model = MockLLMModel()
        self.client = EmbeddingClient(self.model)

    def test_parse_standard_openai_format(self):
        data = {"data": [{"embedding": [0.1, 0.2, 0.3], "index": 0}]}
        result = self.client._parse_openai_response(data)
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0], [0.1, 0.2, 0.3])

    def test_parse_multi_embedding_openai(self):
        data = {
            "data": [
                {"embedding": [0.1, 0.2], "index": 1},
                {"embedding": [0.3, 0.4], "index": 0},
            ]
        }
        result = self.client._parse_openai_response(data)
        self.assertEqual(len(result), 2)
        # Should be sorted by index
        self.assertEqual(result[0], [0.3, 0.4])
        self.assertEqual(result[1], [0.1, 0.2])

    def test_parse_list_of_dicts(self):
        data = [{"embedding": [0.1, 0.2]}, {"embedding": [0.3, 0.4]}]
        result = self.client._parse_openai_response(data)
        self.assertEqual(len(result), 2)

    def test_parse_dict_with_embedding_key(self):
        data = {"embedding": [0.1, 0.2, 0.3]}
        result = self.client._parse_openai_response(data)
        self.assertEqual(len(result), 1)

    def test_parse_dict_with_embeddings_key(self):
        data = {"embeddings": [[0.1, 0.2], [0.3, 0.4]]}
        result = self.client._parse_openai_response(data)
        self.assertEqual(len(result), 2)

    def test_unexpected_format_raises(self):
        with self.assertRaises(ValueError):
            self.client._parse_openai_response({"unexpected": "data"})


class EmbeddingClientDispatchTests(TestCase):
    """Tests for API type dispatch."""

    @patch("library.services.embedding_client.requests.post")
    def test_embed_text_openai_dispatch(self, mock_post):
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "data": [{"embedding": [0.1, 0.2, 0.3], "index": 0}]
        }
        mock_post.return_value = mock_response

        model = MockLLMModel(api_type="openai")
        client = EmbeddingClient(model)
        result = client.embed_text("test text")

        self.assertEqual(result, [0.1, 0.2, 0.3])
        mock_post.assert_called_once()
        call_url = mock_post.call_args[0][0]
        self.assertIn("/embeddings", call_url)

    @patch("library.services.embedding_client.requests.post")
    def test_embed_text_bedrock_dispatch(self, mock_post):
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "embedding": [0.4, 0.5, 0.6],
            "inputTextTokenCount": 5,
        }
        mock_post.return_value = mock_response

        model = MockLLMModel(api_type="bedrock", vector_dimensions=1024)
        model.api.base_url = "https://bedrock-runtime.us-east-1.amazonaws.com"
        client = EmbeddingClient(model)
        result = client.embed_text("test text")

        self.assertEqual(result, [0.4, 0.5, 0.6])
        call_url = mock_post.call_args[0][0]
        self.assertIn("/model/", call_url)
        self.assertIn("/invoke", call_url)

    def test_embed_image_not_multimodal_returns_none(self):
        model = MockLLMModel(supports_multimodal=False)
        client = EmbeddingClient(model)
        result = client.embed_image(b"fake image data", "png")
        self.assertIsNone(result)

    @patch("library.services.embedding_client.requests.post")
    def test_embed_texts_batch(self, mock_post):
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "data": [
                {"embedding": [0.1, 0.2], "index": 0},
                {"embedding": [0.3, 0.4], "index": 1},
            ]
        }
        mock_post.return_value = mock_response

        model = MockLLMModel()
        client = EmbeddingClient(model)
        results = client.embed_texts(["text1", "text2"])

        self.assertEqual(len(results), 2)
