"""
Tests für Inference Service.
"""

import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from llm_gateway.services.inference import (
    InferenceService,
    InferenceResult,
    get_inference_service,
)
from llm_gateway.models.chat import (
    ChatCompletionRequest,
    ChatMessage,
    Usage,
)


class TestInferenceServiceModelMapping:
    """Tests für Model Mapping."""

    def setup_method(self):
        """Setup für jeden Test."""
        self.service = InferenceService()

    def test_map_breakpilot_model_to_ollama(self):
        """Test Mapping von BreakPilot Modell zu Ollama."""
        # Mock Ollama als verfügbares Backend
        with patch.object(self.service, 'config') as mock_config:
            mock_config.ollama = MagicMock()
            mock_config.ollama.name = "ollama"
            mock_config.ollama.enabled = True
            mock_config.vllm = None
            mock_config.anthropic = None
            mock_config.backend_priority = ["ollama", "vllm", "anthropic"]

            actual_model, backend = self.service._map_model_to_backend("breakpilot-teacher-8b")
            assert actual_model == "llama3.1:8b"
            assert backend.name == "ollama"

    def test_map_breakpilot_70b_model(self):
        """Test Mapping von 70B Modell."""
        with patch.object(self.service, 'config') as mock_config:
            mock_config.ollama = MagicMock()
            mock_config.ollama.name = "ollama"
            mock_config.ollama.enabled = True
            mock_config.vllm = None
            mock_config.anthropic = None
            mock_config.backend_priority = ["ollama"]

            actual_model, backend = self.service._map_model_to_backend("breakpilot-teacher-70b")
            assert "70b" in actual_model.lower()

    def test_map_claude_model_to_anthropic(self):
        """Test Mapping von Claude Modell zu Anthropic."""
        with patch.object(self.service, 'config') as mock_config:
            mock_config.ollama = None
            mock_config.vllm = None
            mock_config.anthropic = MagicMock()
            mock_config.anthropic.name = "anthropic"
            mock_config.anthropic.enabled = True
            mock_config.anthropic.default_model = "claude-3-5-sonnet-20241022"
            mock_config.backend_priority = ["anthropic"]

            actual_model, backend = self.service._map_model_to_backend("claude-3-5-sonnet")
            assert backend.name == "anthropic"
            assert "claude" in actual_model.lower()

    def test_map_model_no_backend_available(self):
        """Test Fehler wenn kein Backend verfügbar."""
        with patch.object(self.service, 'config') as mock_config:
            mock_config.ollama = None
            mock_config.vllm = None
            mock_config.anthropic = None
            mock_config.backend_priority = []

            with pytest.raises(ValueError, match="No LLM backend available"):
                self.service._map_model_to_backend("breakpilot-teacher-8b")


class TestInferenceServiceBackendSelection:
    """Tests für Backend-Auswahl."""

    def setup_method(self):
        """Setup für jeden Test."""
        self.service = InferenceService()

    def test_get_available_backend_priority(self):
        """Test Backend-Auswahl nach Priorität."""
        with patch.object(self.service, 'config') as mock_config:
            # Beide Backends verfügbar
            mock_config.ollama = MagicMock()
            mock_config.ollama.enabled = True
            mock_config.vllm = MagicMock()
            mock_config.vllm.enabled = True
            mock_config.anthropic = None
            mock_config.backend_priority = ["vllm", "ollama"]

            backend = self.service._get_available_backend()
            # vLLM hat höhere Priorität
            assert backend == mock_config.vllm

    def test_get_available_backend_fallback(self):
        """Test Fallback wenn primäres Backend nicht verfügbar."""
        with patch.object(self.service, 'config') as mock_config:
            mock_config.ollama = MagicMock()
            mock_config.ollama.enabled = True
            mock_config.vllm = MagicMock()
            mock_config.vllm.enabled = False  # Deaktiviert
            mock_config.anthropic = None
            mock_config.backend_priority = ["vllm", "ollama"]

            backend = self.service._get_available_backend()
            # Ollama als Fallback
            assert backend == mock_config.ollama

    def test_get_available_backend_none_available(self):
        """Test wenn kein Backend verfügbar."""
        with patch.object(self.service, 'config') as mock_config:
            mock_config.ollama = None
            mock_config.vllm = None
            mock_config.anthropic = None
            mock_config.backend_priority = ["ollama", "vllm", "anthropic"]

            backend = self.service._get_available_backend()
            assert backend is None


class TestInferenceResult:
    """Tests für InferenceResult Dataclass."""

    def test_inference_result_creation(self):
        """Test InferenceResult erstellen."""
        result = InferenceResult(
            content="Hello, world!",
            model="llama3.1:8b",
            backend="ollama",
            usage=Usage(prompt_tokens=10, completion_tokens=5, total_tokens=15),
            finish_reason="stop",
        )
        assert result.content == "Hello, world!"
        assert result.model == "llama3.1:8b"
        assert result.backend == "ollama"
        assert result.usage.total_tokens == 15

    def test_inference_result_defaults(self):
        """Test Standardwerte."""
        result = InferenceResult(
            content="Test",
            model="test",
            backend="test",
        )
        assert result.usage is None
        assert result.finish_reason == "stop"


class TestInferenceServiceComplete:
    """Tests für complete() Methode."""

    @pytest.mark.asyncio
    async def test_complete_calls_correct_backend(self):
        """Test dass correct Backend aufgerufen wird."""
        service = InferenceService()

        request = ChatCompletionRequest(
            model="breakpilot-teacher-8b",
            messages=[ChatMessage(role="user", content="Hello")],
        )

        # Mock das Backend
        with patch.object(service, '_map_model_to_backend') as mock_map:
            with patch.object(service, '_call_ollama') as mock_call:
                mock_backend = MagicMock()
                mock_backend.name = "ollama"
                mock_map.return_value = ("llama3.1:8b", mock_backend)
                mock_call.return_value = InferenceResult(
                    content="Hello!",
                    model="llama3.1:8b",
                    backend="ollama",
                )

                response = await service.complete(request)

                mock_call.assert_called_once()
                assert response.choices[0].message.content == "Hello!"


class TestGetInferenceServiceSingleton:
    """Tests für Singleton Pattern."""

    def test_singleton_returns_same_instance(self):
        """Test dass get_inference_service Singleton zurückgibt."""
        service1 = get_inference_service()
        service2 = get_inference_service()
        assert service1 is service2
