"""
Tests for the OpenAI-compatible base provider.

Tests cover:
- Provider class attributes
- LLM creation
- Model listing
- Availability checking
"""

from unittest.mock import Mock, patch

import pytest


class TestOpenAICompatibleProviderAttributes:
    """Tests for OpenAICompatibleProvider class attributes."""

    def test_has_provider_name(self):
        """Provider has provider_name attribute."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        assert hasattr(OpenAICompatibleProvider, "provider_name")
        assert OpenAICompatibleProvider.provider_name == "openai_endpoint"

    def test_has_default_base_url(self):
        """Provider has default_base_url attribute."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        assert hasattr(OpenAICompatibleProvider, "default_base_url")
        assert "api.openai.com" in OpenAICompatibleProvider.default_base_url

    def test_has_default_model(self):
        """Provider has default_model attribute."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        assert hasattr(OpenAICompatibleProvider, "default_model")


class TestOpenAICompatibleProviderCreateLLM:
    """Tests for OpenAICompatibleProvider.create_llm method."""

    def test_create_llm_raises_without_api_key(self):
        """create_llm raises ValueError when API key not configured."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            return_value=None,
        ):
            try:
                OpenAICompatibleProvider.create_llm()
                assert False, "Expected ValueError"
            except ValueError as e:
                assert "API key not configured" in str(e)

    def test_create_llm_uses_default_model(self):
        """Raises ValueError when no model name is provided (no silent default)."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        def mock_get_setting(key, default=None, **kwargs):
            if key == "llm.openai_endpoint.api_key":
                return "test-api-key"
            if key == "llm.max_tokens":
                return None
            return default

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            side_effect=mock_get_setting,
        ):
            with pytest.raises(ValueError, match="model not configured"):
                OpenAICompatibleProvider.create_llm()

    def test_create_llm_uses_specified_model(self):
        """create_llm uses specified model."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        def mock_get_setting(key, default=None, **kwargs):
            if key == "llm.openai_endpoint.api_key":
                return "test-api-key"
            if key == "llm.max_tokens":
                return None
            return default

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            side_effect=mock_get_setting,
        ):
            with patch(
                "local_deep_research.llm.providers.openai_base.ChatOpenAI"
            ) as mock_chat:
                mock_chat.return_value = Mock()

                OpenAICompatibleProvider.create_llm(model_name="gpt-4")

                call_kwargs = mock_chat.call_args[1]
                assert call_kwargs["model"] == "gpt-4"

    def test_create_llm_uses_specified_temperature(self):
        """create_llm uses specified temperature."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        def mock_get_setting(key, default=None, **kwargs):
            if key == "llm.openai_endpoint.api_key":
                return "test-api-key"
            if key == "llm.max_tokens":
                return None
            return default

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            side_effect=mock_get_setting,
        ):
            with patch(
                "local_deep_research.llm.providers.openai_base.ChatOpenAI"
            ) as mock_chat:
                mock_chat.return_value = Mock()

                OpenAICompatibleProvider.create_llm(
                    model_name="test-model", temperature=0.5
                )

                call_kwargs = mock_chat.call_args[1]
                assert call_kwargs["temperature"] == 0.5


class TestOpenAICompatibleProviderIsAvailable:
    """Tests for OpenAICompatibleProvider.is_available method."""

    def test_is_available_with_api_key(self):
        """is_available returns True when API key is configured."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            return_value="test-api-key",
        ):
            result = OpenAICompatibleProvider.is_available()

            assert result is True

    def test_is_available_without_api_key(self):
        """is_available returns False when API key not configured."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            return_value=None,
        ):
            result = OpenAICompatibleProvider.is_available()

            assert result is False

    def test_is_available_handles_exception(self):
        """is_available returns False on exception."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            side_effect=Exception("Test error"),
        ):
            result = OpenAICompatibleProvider.is_available()

            assert result is False


class TestOpenAICompatibleProviderRequiresAuthForModels:
    """Tests for OpenAICompatibleProvider.requires_auth_for_models method."""

    def test_requires_auth_for_models_default(self):
        """requires_auth_for_models returns True by default."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        result = OpenAICompatibleProvider.requires_auth_for_models()

        assert result is True


class TestOpenAICompatibleProviderListModels:
    """Tests for OpenAICompatibleProvider.list_models method."""

    def test_list_models_returns_list(self):
        """list_models returns a list."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch.object(
            OpenAICompatibleProvider, "list_models_for_api", return_value=[]
        ):
            with patch(
                "local_deep_research.config.thread_settings.get_setting_from_snapshot",
                return_value="test-key",
            ):
                result = OpenAICompatibleProvider.list_models()

                assert isinstance(result, list)

    def test_list_models_handles_exception(self):
        """list_models returns empty list on exception."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            side_effect=Exception("Test error"),
        ):
            result = OpenAICompatibleProvider.list_models()

            assert result == []


class TestOpenAICompatibleProviderListModelsForApi:
    """Tests for OpenAICompatibleProvider.list_models_for_api method."""

    def test_list_models_for_api_without_api_key(self):
        """list_models_for_api returns empty list when auth required but no key."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        result = OpenAICompatibleProvider.list_models_for_api(api_key=None)

        assert result == []

    def test_list_models_for_api_with_api_key(self):
        """list_models_for_api calls OpenAI client with provided key."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        mock_model = Mock()
        mock_model.id = "test-model"

        mock_models_response = Mock()
        mock_models_response.data = [mock_model]

        with patch("openai.OpenAI") as mock_openai:
            mock_client = Mock()
            mock_client.models.list.return_value = mock_models_response
            mock_openai.return_value = mock_client

            result = OpenAICompatibleProvider.list_models_for_api(
                api_key="test-key"
            )

            assert len(result) == 1
            assert result[0]["value"] == "test-model"

    def test_list_models_for_api_handles_exception(self):
        """list_models_for_api returns empty list on exception."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch("openai.OpenAI") as mock_openai:
            mock_openai.side_effect = Exception("Connection error")

            result = OpenAICompatibleProvider.list_models_for_api(
                api_key="test-key"
            )

            assert result == []


class TestOpenAICompatibleProviderGetBaseUrlForModels:
    """Tests for OpenAICompatibleProvider._get_base_url_for_models method."""

    def test_get_base_url_uses_default(self):
        """_get_base_url_for_models uses default when no url_setting."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        result = OpenAICompatibleProvider._get_base_url_for_models()

        assert result == OpenAICompatibleProvider.default_base_url


class TestOpenAICompatibleProviderCreateLLMInstance:
    """Tests for OpenAICompatibleProvider._create_llm_instance method."""

    def test_create_llm_instance_uses_dummy_key(self):
        """_create_llm_instance uses dummy key by default."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch(
            "local_deep_research.llm.providers.openai_base.ChatOpenAI"
        ) as mock_chat:
            mock_chat.return_value = Mock()

            OpenAICompatibleProvider._create_llm_instance(
                model_name="test-model"
            )

            call_kwargs = mock_chat.call_args[1]
            assert call_kwargs["api_key"] == "not-required"

    def test_create_llm_instance_uses_provided_key(self):
        """_create_llm_instance uses provided API key."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        with patch(
            "local_deep_research.llm.providers.openai_base.ChatOpenAI"
        ) as mock_chat:
            mock_chat.return_value = Mock()

            OpenAICompatibleProvider._create_llm_instance(
                model_name="test-model", api_key="custom-key"
            )

            call_kwargs = mock_chat.call_args[1]
            assert call_kwargs["api_key"] == "custom-key"


class TestCustomProvider:
    """Tests for creating custom providers inheriting from OpenAICompatibleProvider."""

    def test_custom_provider_inherits_attributes(self):
        """Custom provider inherits base attributes."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        class CustomProvider(OpenAICompatibleProvider):
            provider_name = "Custom"
            api_key_setting = None  # No API key needed
            default_base_url = "http://localhost:8000"

        assert CustomProvider.provider_name == "Custom"
        assert CustomProvider.default_base_url == "http://localhost:8000"

    def test_custom_provider_no_api_key_available(self):
        """Custom provider without api_key_setting is always available."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        class NoAuthProvider(OpenAICompatibleProvider):
            provider_name = "NoAuth"
            api_key_setting = None
            default_base_url = "http://localhost:8000"

        result = NoAuthProvider.is_available()

        assert result is True
