"""
Tests for llm/providers/openai_base.py

Tests cover:
- OpenAICompatibleProvider class methods
- Provider availability checking
- API key handling
- Default model and URL handling
"""

from unittest.mock import Mock, patch
import pytest


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

    def test_class_has_provider_name(self):
        """Test that class 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_class_has_api_key_setting(self):
        """Test that class has api_key_setting attribute."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        assert hasattr(OpenAICompatibleProvider, "api_key_setting")

    def test_class_has_default_base_url(self):
        """Test that class 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_class_has_default_model(self):
        """Test that class 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."""

    @patch("local_deep_research.llm.providers.openai_base.ChatOpenAI")
    @patch(
        "local_deep_research.config.thread_settings.get_setting_from_snapshot"
    )
    def test_create_llm_with_api_key(self, mock_get_setting, mock_chat):
        """Test creating LLM with API key from settings."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        def setting_side_effect(key, *args, **kwargs):
            if "api_key" in key:
                return "test-api-key"
            return None

        mock_get_setting.side_effect = setting_side_effect
        mock_llm = Mock()
        mock_chat.return_value = mock_llm

        result = OpenAICompatibleProvider.create_llm(
            model_name="gpt-4", temperature=0.5
        )

        assert result is mock_llm
        mock_chat.assert_called_once()
        call_kwargs = mock_chat.call_args[1]
        assert call_kwargs["model"] == "gpt-4"
        assert call_kwargs["temperature"] == 0.5

    @patch(
        "local_deep_research.config.thread_settings.get_setting_from_snapshot"
    )
    def test_create_llm_raises_without_api_key(self, mock_get_setting):
        """Test that create_llm raises when API key is missing."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        mock_get_setting.return_value = None

        with pytest.raises(ValueError, match="API key not configured"):
            OpenAICompatibleProvider.create_llm(model_name="gpt-4")

    @patch("local_deep_research.llm.providers.openai_base.ChatOpenAI")
    @patch(
        "local_deep_research.config.thread_settings.get_setting_from_snapshot"
    )
    def test_create_llm_uses_default_model(self, mock_get_setting, mock_chat):
        """Test that create_llm raises ValueError when no model name is provided."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        def setting_side_effect(key, *args, **kwargs):
            if "api_key" in key:
                return "test-api-key"
            return None

        mock_get_setting.side_effect = setting_side_effect
        mock_chat.return_value = Mock()

        with pytest.raises(ValueError, match="model not configured"):
            OpenAICompatibleProvider.create_llm()

    @patch("local_deep_research.llm.providers.openai_base.ChatOpenAI")
    @patch(
        "local_deep_research.config.thread_settings.get_setting_from_snapshot"
    )
    def test_create_llm_uses_default_temperature(
        self, mock_get_setting, mock_chat
    ):
        """Test that create_llm uses default temperature of 0.7."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        def setting_side_effect(key, *args, **kwargs):
            if "api_key" in key:
                return "test-api-key"
            return None

        mock_get_setting.side_effect = setting_side_effect
        mock_chat.return_value = Mock()

        OpenAICompatibleProvider.create_llm(model_name="test")

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

    @patch("local_deep_research.llm.providers.openai_base.ChatOpenAI")
    @patch(
        "local_deep_research.config.thread_settings.get_setting_from_snapshot"
    )
    def test_create_llm_accepts_custom_base_url(
        self, mock_get_setting, mock_chat
    ):
        """Test that create_llm accepts custom base_url."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        def setting_side_effect(key, *args, **kwargs):
            if "api_key" in key:
                return "test-api-key"
            return None

        mock_get_setting.side_effect = setting_side_effect
        mock_chat.return_value = Mock()

        OpenAICompatibleProvider.create_llm(
            model_name="test", base_url="https://custom.api.com/v1"
        )

        call_kwargs = mock_chat.call_args[1]
        assert "custom.api.com" in call_kwargs["base_url"]


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

    @patch(
        "local_deep_research.config.thread_settings.get_setting_from_snapshot"
    )
    def test_is_available_returns_true_with_api_key(self, mock_get_setting):
        """Test that is_available returns True when API key is set."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        mock_get_setting.return_value = "test-api-key"

        assert OpenAICompatibleProvider.is_available() is True

    @patch(
        "local_deep_research.config.thread_settings.get_setting_from_snapshot"
    )
    def test_is_available_returns_false_without_api_key(self, mock_get_setting):
        """Test that is_available returns False when API key is not set."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        mock_get_setting.return_value = None

        assert OpenAICompatibleProvider.is_available() is False


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

    @patch("local_deep_research.llm.providers.openai_base.ChatOpenAI")
    def test_create_llm_instance_uses_provided_api_key(self, mock_chat):
        """Test that _create_llm_instance uses provided API key."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        mock_chat.return_value = Mock()

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

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

    @patch("local_deep_research.llm.providers.openai_base.ChatOpenAI")
    def test_create_llm_instance_uses_dummy_key_if_not_provided(
        self, mock_chat
    ):
        """Test that _create_llm_instance uses dummy key if none provided."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        mock_chat.return_value = Mock()

        OpenAICompatibleProvider._create_llm_instance(model_name="test")

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


class TestSubclassImplementation:
    """Tests for subclass implementation pattern."""

    def test_subclass_can_override_provider_name(self):
        """Test that subclass can override provider_name."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        class CustomProvider(OpenAICompatibleProvider):
            provider_name = "custom_provider"

        assert CustomProvider.provider_name == "custom_provider"

    def test_subclass_can_override_default_model(self):
        """Test that subclass can override default_model."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        class CustomProvider(OpenAICompatibleProvider):
            default_model = "custom-model-v1"

        assert CustomProvider.default_model == "custom-model-v1"

    def test_subclass_can_override_api_key_setting(self):
        """Test that subclass can override api_key_setting."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        class CustomProvider(OpenAICompatibleProvider):
            api_key_setting = "llm.custom.api_key"

        assert CustomProvider.api_key_setting == "llm.custom.api_key"

    def test_subclass_can_set_no_api_key_required(self):
        """Test that subclass can disable API key requirement."""
        from local_deep_research.llm.providers.openai_base import (
            OpenAICompatibleProvider,
        )

        class LocalProvider(OpenAICompatibleProvider):
            api_key_setting = None  # No API key required

        assert LocalProvider.api_key_setting is None
