"""
Integration tests for LLM provider configuration and execution.
Tests the full flow from API key configuration to research execution.
"""

import os
import pytest
from unittest.mock import Mock, patch
from local_deep_research.settings import SettingsManager

# Skip all tests in this module when using fallback LLM
pytestmark = pytest.mark.skipif(
    os.environ.get("LDR_USE_FALLBACK_LLM", "") == "true",
    reason="These tests require real LLM providers, not fallback",
)


class TestLLMProviderIntegration:
    """Test complete LLM provider integration scenarios."""

    @pytest.fixture
    def settings_dict(self):
        """Create a dictionary of settings."""
        return {
            "llm.provider": "openai_endpoint",
            "llm.model": "claude-3-sonnet",
            "llm.temperature": 0.7,
            "llm.openai_endpoint.api_key": "sk-openrouter-test-key",
            "llm.openai_endpoint.url": "https://openrouter.ai/api/v1",
            "llm.openai_endpoint.model": "claude-3-sonnet",
            "llm.supports_max_tokens": True,
            "llm.max_tokens": 4096,
            "llm.context_window_unrestricted": True,
            "llm.openai.api_key": None,
            "llm.openai.api_base": None,
            "llm.openai.organization": None,
            "llm.anthropic.api_key": None,
            "llm.streaming": False,
            "llm.max_retries": None,
            "llm.request_timeout": None,
            "llm.ollama.url": "http://localhost:11434",
            "llm.lmstudio.url": "http://localhost:1234",
            "llm.llamacpp_model_path": None,
            "app.lock_settings": False,
            "rate_limiting.llm_enabled": False,
            "search.tool": "searxng",
            "search.iterations": 5,
            "search.questions_per_iteration": 3,
            "search.max_results": 10,
            "search.enable_direct_summary": True,
            "search.enable_search_engine": True,
            "search.additional_results": 3,
            "search.timeout": 15,
            "search.region": "en-US",
            "search.proxy": None,
            "search.validate_sources": True,
            "search.enable_think_tags": False,
            "search.require_all_sources": False,
            "search.max_backoff_time": 300,
            "search.time_period": "all",
            "search.safe_search": True,
            "search.snippets_only": True,
            "search.search_language": "en",
            "search.max_filtered_results": 10,
            "search.smart_search.use_query_improvement": True,
            "search.smart_search.use_document_relevance": True,
            "search.smart_search.use_answer_extraction": True,
            "search.smart_search.use_semantic_cache": True,
            "search.smart_search.use_result_reranking": True,
            "search.smart_search.use_query_suggestion": True,
            "llm.local_context_window_size": 4096,
            "llm.context_window_size": 128000,
        }

    @pytest.fixture
    def mock_session(self, settings_dict):
        """Create a mock database session with settings."""
        session = Mock()

        # Store settings that can be modified
        current_settings = settings_dict.copy()

        # Helper to get settings
        def get_setting_mock(key, default=None, check_env=True):
            return current_settings.get(key, default)

        # Helper to set settings
        def set_setting_mock(key, value, commit=True):
            current_settings[key] = value
            return True

        # Helper to get all settings
        def get_all_settings_mock():
            # Return in the format expected by the API
            result = {}
            for key, value in current_settings.items():
                result[key] = {
                    "value": value,
                    "type": "STRING",
                    "name": key,
                    "description": f"Setting for {key}",
                    "category": key.split(".")[0],
                    "ui_element": "text",
                    "visible": True,
                    "editable": True,
                }
            return result

        # Store these helpers on the session for patching
        session._get_setting = get_setting_mock
        session._set_setting = set_setting_mock
        session._get_all_settings = get_all_settings_mock
        session._current_settings = current_settings

        return session

    def test_configure_openrouter_api_key(self, mock_session):
        """Test configuring OpenRouter API key through settings."""
        # Create settings manager with patched methods
        with patch.object(
            SettingsManager,
            "get_setting",
            side_effect=mock_session._get_setting,
        ):
            with patch.object(
                SettingsManager,
                "set_setting",
                side_effect=mock_session._set_setting,
            ):
                settings_manager = SettingsManager(None)

                # Update API key setting
                new_api_key = "sk-openrouter-new-test-key"
                success = settings_manager.set_setting(
                    "llm.openai_endpoint.api_key", new_api_key
                )
                assert success

                # Verify setting was updated
                stored_value = settings_manager.get_setting(
                    "llm.openai_endpoint.api_key"
                )
                assert stored_value == new_api_key

    def test_research_with_configured_openrouter(self, mock_session):
        """Test running research with configured OpenRouter API."""
        from local_deep_research.api.research_functions import quick_summary

        # Create settings manager with patched methods
        with patch.object(
            SettingsManager,
            "get_all_settings",
            side_effect=mock_session._get_all_settings,
        ):
            settings_manager = SettingsManager(None)
            settings_snapshot = settings_manager.get_all_settings()

            # Verify settings are correct - get_all_settings returns nested dicts
            assert (
                settings_snapshot["llm.provider"]["value"] == "openai_endpoint"
            )
            assert (
                settings_snapshot["llm.openai_endpoint.api_key"]["value"]
                == "sk-openrouter-test-key"
            )

            # Mock the LLM and search
            with patch(
                "local_deep_research.config.llm_config.ChatOpenAI"
            ) as mock_openai:
                with patch(
                    "local_deep_research.api.research_functions.AdvancedSearchSystem"
                ) as mock_search_system:
                    # Setup mock LLM
                    mock_llm_instance = Mock()
                    mock_response = Mock()
                    mock_response.content = "Based on my research: Test topic is important because..."
                    mock_llm_instance.invoke.return_value = mock_response
                    mock_openai.return_value = mock_llm_instance

                    # Setup mock search system
                    mock_system_instance = Mock()
                    mock_system_instance.analyze_topic.return_value = {
                        "current_knowledge": "Based on my research: Test topic is important because...",
                        "sources": ["https://example.com/test-topic"],
                        "all_links_of_system": [
                            "https://example.com/test-topic"
                        ],
                        "findings": [],
                        "iterations": 2,
                        "questions": {},
                    }
                    mock_search_system.return_value = mock_system_instance

                    # Run research
                    result = quick_summary(
                        query="Explain test topic",
                        iterations=2,
                        questions_per_iteration=3,
                        settings_snapshot=settings_snapshot,
                    )

                    # Verify OpenRouter was configured correctly
                    assert mock_openai.called
                    call_args = mock_openai.call_args
                    assert (
                        call_args.kwargs["api_key"] == "sk-openrouter-test-key"
                    )
                    assert (
                        call_args.kwargs["openai_api_base"]
                        == "https://openrouter.ai/api/v1"
                    )
                    assert call_args.kwargs["model"] == "claude-3-sonnet"

                    # Verify research completed
                    assert "summary" in result
                    assert "sources" in result

    def test_switching_providers_dynamically(self, mock_session):
        """Test switching between providers dynamically."""
        from local_deep_research.config.llm_config import get_llm

        with patch.object(
            SettingsManager,
            "get_setting",
            side_effect=mock_session._get_setting,
        ):
            with patch.object(
                SettingsManager,
                "set_setting",
                side_effect=mock_session._set_setting,
            ):
                with patch.object(
                    SettingsManager,
                    "get_all_settings",
                    side_effect=mock_session._get_all_settings,
                ):
                    settings_manager = SettingsManager(None)

                    # Test 1: OpenRouter configuration
                    settings_manager.set_setting(
                        "llm.provider", "openai_endpoint"
                    )
                    settings_manager.set_setting(
                        "llm.openai_endpoint.api_key", "sk-openrouter-key"
                    )
                    settings_snapshot = settings_manager.get_all_settings()

                    with patch(
                        "local_deep_research.config.llm_config.ChatOpenAI"
                    ) as mock_openai:
                        mock_openai.return_value = Mock()
                        get_llm(settings_snapshot=settings_snapshot)
                        assert (
                            mock_openai.call_args.kwargs["api_key"]
                            == "sk-openrouter-key"
                        )

                    # Test 2: Switch to OpenAI
                    settings_manager.set_setting("llm.provider", "openai")
                    settings_manager.set_setting(
                        "llm.openai.api_key", "sk-openai-key"
                    )
                    settings_snapshot = settings_manager.get_all_settings()

                    with patch(
                        "local_deep_research.config.llm_config.ChatOpenAI"
                    ) as mock_openai:
                        mock_openai.return_value = Mock()
                        get_llm(settings_snapshot=settings_snapshot)
                        assert (
                            mock_openai.call_args.kwargs["api_key"]
                            == "sk-openai-key"
                        )
                        assert (
                            "openai_api_base"
                            not in mock_openai.call_args.kwargs
                        )

                    # Test 3: Switch to Anthropic
                    settings_manager.set_setting("llm.provider", "anthropic")
                    settings_manager.set_setting(
                        "llm.anthropic.api_key", "sk-anthropic-key"
                    )
                    settings_snapshot = settings_manager.get_all_settings()

                    with patch(
                        "local_deep_research.config.llm_config.ChatAnthropic"
                    ) as mock_anthropic:
                        mock_anthropic.return_value = Mock()
                        get_llm(settings_snapshot=settings_snapshot)
                        assert (
                            mock_anthropic.call_args.kwargs["anthropic_api_key"]
                            == "sk-anthropic-key"
                        )

    def test_api_key_validation_before_research(self, mock_session):
        """Test that API key presence is validated before starting research."""
        from local_deep_research.config.llm_config import (
            is_openai_endpoint_available,
        )

        with patch.object(
            SettingsManager,
            "set_setting",
            side_effect=mock_session._set_setting,
        ):
            settings_manager = SettingsManager(None)

            # With API key
            settings_manager.set_setting(
                "llm.openai_endpoint.api_key", "sk-test-key"
            )
            settings_with_key = {
                "llm.openai_endpoint.api_key": {
                    "value": "sk-test-key",
                    "type": "str",
                }
            }
            assert (
                is_openai_endpoint_available(
                    settings_snapshot=settings_with_key
                )
                is True
            )

            # Without API key
            settings_manager.set_setting("llm.openai_endpoint.api_key", None)
            settings_without_key = {
                "llm.openai_endpoint.api_key": {"value": None, "type": "str"}
            }
            assert (
                is_openai_endpoint_available(
                    settings_snapshot=settings_without_key
                )
                is False
            )

    def test_custom_model_configuration(self, mock_session):
        """Test configuring custom models with API endpoints."""
        with patch.object(
            SettingsManager,
            "get_setting",
            side_effect=mock_session._get_setting,
        ):
            with patch.object(
                SettingsManager,
                "set_setting",
                side_effect=mock_session._set_setting,
            ):
                with patch.object(
                    SettingsManager,
                    "get_all_settings",
                    side_effect=mock_session._get_all_settings,
                ):
                    settings_manager = SettingsManager(None)

                    # Configure for a specific model on OpenRouter
                    settings_manager.set_setting(
                        "llm.provider", "openai_endpoint"
                    )
                    settings_manager.set_setting(
                        "llm.model", "anthropic/claude-3-opus"
                    )
                    settings_manager.set_setting(
                        "llm.openai_endpoint.api_key", "sk-openrouter-key"
                    )
                    settings_manager.set_setting(
                        "llm.openai_endpoint.url",
                        "https://openrouter.ai/api/v1",
                    )
                    settings_manager.set_setting("llm.temperature", 0.3)

                    settings_snapshot = settings_manager.get_all_settings()

                    with patch(
                        "local_deep_research.config.llm_config.ChatOpenAI"
                    ) as mock_openai:
                        mock_openai.return_value = Mock()

                        from local_deep_research.config.llm_config import (
                            get_llm,
                        )

                        get_llm(settings_snapshot=settings_snapshot)

                        # Verify model configuration
                        call_args = mock_openai.call_args
                        assert (
                            call_args.kwargs["model"]
                            == "anthropic/claude-3-opus"
                        )
                        assert call_args.kwargs["temperature"] == 0.3
                        assert (
                            call_args.kwargs["api_key"] == "sk-openrouter-key"
                        )
                        assert (
                            call_args.kwargs["openai_api_base"]
                            == "https://openrouter.ai/api/v1"
                        )

    @pytest.mark.skip(reason="Requires actual database session with encryption")
    def test_benchmark_with_api_configured_llm(self, mock_session):
        """Test running benchmarks with API-configured LLM."""
        from local_deep_research.benchmarks.web_api.benchmark_service import (
            BenchmarkService,
        )

        with patch.object(
            SettingsManager,
            "set_setting",
            side_effect=mock_session._set_setting,
        ):
            with patch.object(
                SettingsManager,
                "get_all_settings",
                side_effect=mock_session._get_all_settings,
            ):
                settings_manager = SettingsManager(None)
                settings_manager.set_setting("llm.provider", "openai_endpoint")
                settings_manager.set_setting(
                    "llm.openai_endpoint.api_key", "sk-benchmark-key"
                )

                benchmark_service = BenchmarkService()

                # Create benchmark configuration
                search_config = {
                    "iterations": 2,
                    "questions_per_iteration": 3,
                    "search_tool": "searxng",
                    "provider": "openai_endpoint",
                    "model_name": "claude-3-sonnet",
                }

                evaluation_config = {
                    "provider": "openai_endpoint",
                    "model_name": "claude-3-sonnet",
                }

                datasets_config = {"simpleqa": {"count": 5}}

                # Test benchmark creation
                with patch.object(
                    settings_manager, "get_all_settings"
                ) as mock_get_settings:
                    mock_get_settings.return_value = (
                        settings_manager.get_all_settings()
                    )

                    benchmark_id = benchmark_service.create_benchmark_run(
                        run_name="Test API Key Benchmark",
                        search_config=search_config,
                        evaluation_config=evaluation_config,
                        datasets_config=datasets_config,
                        username="testuser",
                    )

                    assert isinstance(benchmark_id, int)


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
