"""Integration tests: full embed rotation flow with mocked HTTP."""

from __future__ import annotations

import httpx
import pytest
import respx

from llm_rotator import LLMRotator, OpenAIClient, RotatorConfig
from llm_rotator._types import EmbeddingResponse
from llm_rotator.clients.gemini import GeminiClient
from llm_rotator.exceptions import AllAttemptsFailedError


def _openai_embedding_response(model: str = "text-embedding-3-small") -> dict:
    return {
        "object": "list",
        "model": model,
        "data": [
            {"object": "embedding", "index": 0, "embedding": [0.1, 0.2, 0.3]},
        ],
        "usage": {"prompt_tokens": 5, "total_tokens": 5},
    }


def _gemini_embedding_response() -> dict:
    return {
        "embeddings": [
            {"values": [0.01, 0.02, 0.03]},
        ]
    }


class TestEmbedRotationHappyPath:
    @respx.mock
    async def test_embed_happy_path(self):
        respx.post("https://api-a.example.com/v1/embeddings").mock(
            return_value=httpx.Response(200, json=_openai_embedding_response())
        )

        config = RotatorConfig(
            providers=[
                {
                    "name": "provider_a",
                    "client_type": "openai",
                    "priority": 1,
                    "base_url": "https://api-a.example.com/v1",
                    "models": ["text-embedding-3-small"],
                    "keys": [{"token": "sk-a1", "alias": "a_key1"}],
                }
            ]
        )
        rotator = LLMRotator(config, clients={"openai": OpenAIClient()})

        result = await rotator.embed(input=["hello world"])

        assert isinstance(result, EmbeddingResponse)
        assert result.embeddings == [[0.1, 0.2, 0.3]]
        assert result.model == "text-embedding-3-small"


class TestEmbedRotationFallback:
    @respx.mock
    async def test_fallback_on_server_error(self):
        """First provider 500 → fallback to second provider."""
        respx.post("https://api-a.example.com/v1/embeddings").mock(
            return_value=httpx.Response(500, json={"error": {"message": "Internal"}})
        )
        respx.post("https://api-b.example.com/v1/embeddings").mock(
            return_value=httpx.Response(200, json=_openai_embedding_response("backup-model"))
        )

        config = RotatorConfig(
            providers=[
                {
                    "name": "provider_a",
                    "client_type": "openai",
                    "priority": 1,
                    "base_url": "https://api-a.example.com/v1",
                    "models": ["text-embedding-3-small"],
                    "keys": [{"token": "sk-a1", "alias": "a_key1"}],
                },
                {
                    "name": "provider_b",
                    "client_type": "openai",
                    "priority": 2,
                    "base_url": "https://api-b.example.com/v1",
                    "models": ["backup-model"],
                    "keys": [{"token": "sk-b1", "alias": "b_key1"}],
                },
            ]
        )
        rotator = LLMRotator(config, clients={"openai": OpenAIClient()})

        result = await rotator.embed(input=["test"])
        assert result.model == "backup-model"

    @respx.mock
    async def test_fallback_on_rate_limit(self):
        """First provider 429 → fallback to second."""
        respx.post("https://api-a.example.com/v1/embeddings").mock(
            return_value=httpx.Response(
                429,
                json={"error": {"message": "rate limit"}},
                headers={"retry-after": "60"},
            )
        )
        respx.post("https://api-b.example.com/v1/embeddings").mock(
            return_value=httpx.Response(200, json=_openai_embedding_response())
        )

        config = RotatorConfig(
            providers=[
                {
                    "name": "pa",
                    "client_type": "openai",
                    "priority": 1,
                    "base_url": "https://api-a.example.com/v1",
                    "models": ["emb-model"],
                    "keys": [{"token": "sk-a1", "alias": "a1"}],
                },
                {
                    "name": "pb",
                    "client_type": "openai",
                    "priority": 2,
                    "base_url": "https://api-b.example.com/v1",
                    "models": ["emb-model"],
                    "keys": [{"token": "sk-b1", "alias": "b1"}],
                },
            ]
        )
        rotator = LLMRotator(config, clients={"openai": OpenAIClient()})

        result = await rotator.embed(input=["test"])
        assert isinstance(result, EmbeddingResponse)


class TestEmbedMultiProvider:
    @respx.mock
    async def test_openai_and_gemini_fallback(self):
        """OpenAI fails → fallback to Gemini embed."""
        respx.post("https://api-a.example.com/v1/embeddings").mock(
            return_value=httpx.Response(500, json={"error": {"message": "down"}})
        )
        respx.post(
            "https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:batchEmbedContents"
        ).mock(return_value=httpx.Response(200, json=_gemini_embedding_response()))

        config = RotatorConfig(
            providers=[
                {
                    "name": "openai_provider",
                    "client_type": "openai",
                    "priority": 1,
                    "base_url": "https://api-a.example.com/v1",
                    "models": ["text-embedding-3-small"],
                    "keys": [{"token": "sk-a1", "alias": "a_key1"}],
                },
                {
                    "name": "gemini_provider",
                    "client_type": "gemini",
                    "priority": 2,
                    "models": ["text-embedding-004"],
                    "keys": [{"token": "AIzaSyAB", "alias": "g_key1"}],
                },
            ]
        )
        rotator = LLMRotator(
            config,
            clients={"openai": OpenAIClient(), "gemini": GeminiClient()},
        )

        result = await rotator.embed(input=["test"])
        assert result.provider == "gemini"
        assert result.embeddings == [[0.01, 0.02, 0.03]]


class TestEmbedAllFail:
    @respx.mock
    async def test_all_providers_fail(self):
        respx.post("https://api-a.example.com/v1/embeddings").mock(
            return_value=httpx.Response(500, json={"error": {"message": "down"}})
        )

        config = RotatorConfig(
            providers=[
                {
                    "name": "pa",
                    "client_type": "openai",
                    "priority": 1,
                    "base_url": "https://api-a.example.com/v1",
                    "models": ["emb-model"],
                    "keys": [{"token": "sk-a1", "alias": "a1"}],
                },
            ]
        )
        rotator = LLMRotator(config, clients={"openai": OpenAIClient()})

        with pytest.raises(AllAttemptsFailedError):
            await rotator.embed(input=["test"])
