"""Tests for xAI Grok LLM provider."""

import pytest
from unittest.mock import Mock, patch

from local_deep_research.llm.providers.implementations.xai import (
    XAIProvider,
    create_xai_llm,
    is_xai_available,
    register_xai_provider,
)


class TestXAIProviderMetadata:
    """Tests for XAIProvider class metadata."""

    def test_provider_name(self):
        """Provider name is correct."""
        assert XAIProvider.provider_name == "xAI Grok"

    def test_provider_key(self):
        """Provider key is correct."""
        assert XAIProvider.provider_key == "XAI"

    def test_is_cloud(self):
        """xAI is a cloud provider."""
        assert XAIProvider.is_cloud is True

    def test_region(self):
        """Region is US."""
        assert XAIProvider.region == "US"

    def test_country(self):
        """Country is United States."""
        assert XAIProvider.country == "United States"

    def test_data_location(self):
        """Data location is United States."""
        assert XAIProvider.data_location == "United States"

    def test_company_name(self):
        """Company name is xAI."""
        assert XAIProvider.company_name == "xAI"

    def test_api_key_setting(self):
        """API key setting is correct."""
        assert XAIProvider.api_key_setting == "llm.xai.api_key"

    def test_default_model(self):
        """Default model is grok-beta."""
        assert XAIProvider.default_model is not None
        assert "grok" in XAIProvider.default_model.lower()

    def test_default_base_url(self):
        """Default base URL is correct."""
        assert "x.ai" in XAIProvider.default_base_url


class TestXAICreateLLM:
    """Tests for create_llm method."""

    def test_create_llm_raises_without_api_key(self):
        """Raises ValueError when API key not configured."""
        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.return_value = None

            with pytest.raises(ValueError) as exc_info:
                XAIProvider.create_llm()

            assert "api key" in str(exc_info.value).lower()

    def test_create_llm_with_valid_api_key(self):
        """Successfully creates ChatOpenAI instance with valid API key."""

        def mock_get_setting_side_effect(key, default=None, *args, **kwargs):
            settings_map = {
                "llm.xai.api_key": "test-xai-key",
                "llm.max_tokens": None,
                "llm.streaming": None,
                "llm.max_retries": None,
                "llm.request_timeout": None,
            }
            return settings_map.get(key, default)

        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.side_effect = mock_get_setting_side_effect

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

                result = XAIProvider.create_llm()

                assert result is mock_llm
                mock_chat.assert_called_once()

    def test_create_llm_uses_default_model_when_none(self):
        """Uses default model when none specified."""

        def mock_get_setting_side_effect(key, default=None, *args, **kwargs):
            settings_map = {
                "llm.xai.api_key": "test-key",
                "llm.max_tokens": None,
                "llm.streaming": None,
                "llm.max_retries": None,
                "llm.request_timeout": None,
            }
            return settings_map.get(key, default)

        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.side_effect = mock_get_setting_side_effect

            with patch(
                "local_deep_research.llm.providers.openai_base.ChatOpenAI"
            ) as mock_chat:
                XAIProvider.create_llm()

                call_kwargs = mock_chat.call_args[1]
                assert call_kwargs["model"] == XAIProvider.default_model

    def test_create_llm_with_custom_model(self):
        """Uses custom model when specified."""

        def mock_get_setting_side_effect(key, default=None, *args, **kwargs):
            settings_map = {
                "llm.xai.api_key": "test-key",
                "llm.max_tokens": None,
                "llm.streaming": None,
                "llm.max_retries": None,
                "llm.request_timeout": None,
            }
            return settings_map.get(key, default)

        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.side_effect = mock_get_setting_side_effect

            with patch(
                "local_deep_research.llm.providers.openai_base.ChatOpenAI"
            ) as mock_chat:
                XAIProvider.create_llm(model_name="grok-2")

                call_kwargs = mock_chat.call_args[1]
                assert call_kwargs["model"] == "grok-2"

    def test_create_llm_passes_temperature(self):
        """Passes temperature parameter."""

        def mock_get_setting_side_effect(key, default=None, *args, **kwargs):
            settings_map = {
                "llm.xai.api_key": "test-key",
                "llm.max_tokens": None,
                "llm.streaming": None,
                "llm.max_retries": None,
                "llm.request_timeout": None,
            }
            return settings_map.get(key, default)

        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.side_effect = mock_get_setting_side_effect

            with patch(
                "local_deep_research.llm.providers.openai_base.ChatOpenAI"
            ) as mock_chat:
                XAIProvider.create_llm(temperature=0.8)

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

    def test_create_llm_uses_xai_base_url(self):
        """Uses xAI's base URL."""

        def mock_get_setting_side_effect(key, default=None, *args, **kwargs):
            settings_map = {
                "llm.xai.api_key": "test-key",
                "llm.max_tokens": None,
                "llm.streaming": None,
                "llm.max_retries": None,
                "llm.request_timeout": None,
            }
            return settings_map.get(key, default)

        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.side_effect = mock_get_setting_side_effect

            with patch(
                "local_deep_research.llm.providers.openai_base.ChatOpenAI"
            ) as mock_chat:
                XAIProvider.create_llm()

                call_kwargs = mock_chat.call_args[1]
                assert "x.ai" in call_kwargs["base_url"]


class TestXAIIsAvailable:
    """Tests for is_available method."""

    def test_is_available_true_when_key_exists(self):
        """Returns True when API key is configured."""
        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.return_value = "test-key"

            result = XAIProvider.is_available()
            assert result is True

    def test_is_available_false_when_no_key(self):
        """Returns False when API key is not configured."""
        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.return_value = None

            result = XAIProvider.is_available()
            assert result is False

    def test_is_available_false_when_empty_key(self):
        """Returns False when API key is empty string."""
        with patch(
            "local_deep_research.llm.providers.openai_base.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.return_value = ""

            result = XAIProvider.is_available()
            assert result is False


class TestXAIRequiresAuth:
    """Tests for requires_auth_for_models method."""

    def test_requires_auth_for_models(self):
        """xAI requires authentication for listing models."""
        assert XAIProvider.requires_auth_for_models() is True


class TestXAIBackwardCompatibility:
    """Tests for backward compatibility functions."""

    def test_create_xai_llm_function(self):
        """create_xai_llm() delegates to XAIProvider."""
        with patch.object(XAIProvider, "create_llm") as mock_create:
            mock_llm = Mock()
            mock_create.return_value = mock_llm

            result = create_xai_llm(model_name="grok-2", temperature=0.5)

            mock_create.assert_called_once_with("grok-2", 0.5)
            assert result is mock_llm

    def test_is_xai_available_function(self):
        """is_xai_available() delegates to XAIProvider."""
        with patch.object(XAIProvider, "is_available") as mock_available:
            mock_available.return_value = True

            result = is_xai_available()

            mock_available.assert_called_once()
            assert result is True

    def test_register_xai_provider_function(self):
        """register_xai_provider() registers with registry."""
        with patch(
            "local_deep_research.llm.providers.implementations.xai.register_llm"
        ) as mock_register:
            register_xai_provider()

            mock_register.assert_called_once_with("xai", create_xai_llm)
