"""End-to-end tests for API settings in research workflows."""

import pytest
from unittest.mock import patch, MagicMock

from src.local_deep_research.api import quick_summary, detailed_research
from src.local_deep_research.api.settings_utils import create_settings_snapshot


class TestE2EResearchWithSettings:
    """Test end-to-end research workflows with various settings."""

    @pytest.mark.skip(
        reason="Requires complex thread context setup - tested via unit tests"
    )
    @patch("src.local_deep_research.config.llm_config.get_llm")
    @patch(
        "src.local_deep_research.web_search_engines.search_engine_factory.get_search"
    )
    def test_quick_summary_full_flow(self, mock_get_search, mock_get_llm):
        """Test quick_summary with full settings propagation."""
        # Mock LLM
        mock_llm = MagicMock()
        mock_llm.invoke.return_value = MagicMock(content="Test summary")
        mock_get_llm.return_value = mock_llm

        # Mock search engine
        mock_search = MagicMock()
        mock_search.search.return_value = {
            "results": [
                {
                    "title": "Result 1",
                    "url": "http://example.com/1",
                    "snippet": "Snippet 1",
                },
                {
                    "title": "Result 2",
                    "url": "http://example.com/2",
                    "snippet": "Snippet 2",
                },
            ]
        }
        mock_get_search.return_value = mock_search

        # Run quick summary with custom settings
        result = quick_summary(
            "What is quantum computing?",
            provider="anthropic",
            api_key="test-key",
            temperature=0.5,
            max_search_results=10,
            settings_override={
                "llm.anthropic.model": "claude-3-opus-20240229",
                "search.tool": "duckduckgo",
                "search.region": "us-en",
            },
        )

        # Verify LLM was configured correctly
        mock_get_llm.assert_called()

        # Verify search was configured correctly
        mock_get_search.assert_called()
        search_call_kwargs = mock_get_search.call_args[1]
        assert search_call_kwargs["search_tool"] == "duckduckgo"
        assert search_call_kwargs["max_results"] == 10
        assert search_call_kwargs["region"] == "us-en"

        # Verify result structure
        assert "summary" in result
        assert "findings" in result
        assert "iterations" in result

    @pytest.mark.skip(
        reason="Requires complex thread context setup - tested via unit tests"
    )
    @patch("src.local_deep_research.config.llm_config.get_llm")
    @patch(
        "src.local_deep_research.web_search_engines.search_engine_factory.get_search"
    )
    def test_detailed_research_full_flow(self, mock_get_search, mock_get_llm):
        """Test detailed_research with comprehensive settings."""
        # Mock LLM with different responses
        mock_llm = MagicMock()
        mock_llm.invoke.side_effect = [
            MagicMock(content="Initial analysis"),
            MagicMock(content="Deeper analysis"),
            MagicMock(content="Final synthesis"),
        ]
        mock_get_llm.return_value = mock_llm

        # Mock search with evolving results
        mock_search = MagicMock()
        mock_search.search.side_effect = [
            {
                "results": [
                    {
                        "title": f"Initial {i}",
                        "url": f"http://example.com/init{i}",
                        "snippet": f"Initial snippet {i}",
                    }
                    for i in range(3)
                ]
            },
            {
                "results": [
                    {
                        "title": f"Deep {i}",
                        "url": f"http://example.com/deep{i}",
                        "snippet": f"Deep snippet {i}",
                    }
                    for i in range(5)
                ]
            },
        ]
        mock_get_search.return_value = mock_search

        # Run detailed research with custom settings
        result = detailed_research(
            "Explain the applications of quantum computing in cryptography",
            provider="openai",
            api_key="test-key",
            temperature=0.3,
            settings_override={
                "research.max_iterations": 3,
                "search.max_results": 20,
                "search.engines.arxiv.enabled": True,
                "llm.max_tokens": 4000,
            },
        )

        # Verify multiple search calls were made
        assert mock_search.search.call_count >= 1

        # Verify result has detailed structure
        assert "summary" in result
        assert "findings" in result
        assert "iterations" in result
        assert result["iterations"] > 1  # Should do multiple iterations

    def test_settings_isolation_between_calls(self):
        """Test that settings don't leak between API calls."""
        with patch(
            "src.local_deep_research.api.research_functions._init_search_system"
        ) as mock_init:
            mock_system = MagicMock()
            mock_system.analyze_topic.return_value = {
                "current_knowledge": "Summary",
                "findings": [],
                "iterations": 1,
                "questions": {},
                "formatted_findings": "Findings",
                "all_links_of_system": [],
            }
            mock_init.return_value = mock_system

            # First call with one set of settings
            _ = quick_summary("Query 1", provider="openai", temperature=0.9)

            # Second call with different settings
            _ = quick_summary("Query 2", provider="anthropic", temperature=0.1)

            # Verify each call got its own settings
            call1_settings = mock_init.call_args_list[0][1]["settings_snapshot"]
            call2_settings = mock_init.call_args_list[1][1]["settings_snapshot"]

            assert call1_settings["llm.provider"]["value"] == "openai"
            assert call1_settings["llm.temperature"]["value"] == 0.9

            assert call2_settings["llm.provider"]["value"] == "anthropic"
            assert call2_settings["llm.temperature"]["value"] == 0.1


class TestMultiProviderScenarios:
    """Test scenarios with multiple LLM providers."""

    @patch("src.local_deep_research.config.llm_config.get_llm")
    def test_provider_fallback_scenario(self, mock_get_llm):
        """Test fallback between providers based on settings."""
        # Simulate primary provider failure
        primary_llm = MagicMock()
        primary_llm.invoke.side_effect = Exception("API rate limit exceeded")

        # Simulate fallback provider success
        fallback_llm = MagicMock()
        fallback_llm.invoke.return_value = MagicMock(
            content="Fallback response"
        )

        # Configure mock to return different LLMs based on provider
        def get_llm_side_effect(*args, **kwargs):
            snapshot = kwargs.get("settings_snapshot", {})
            provider = snapshot.get("llm.provider", {}).get("value", "")
            if provider == "openai":
                return primary_llm
            return fallback_llm

        mock_get_llm.side_effect = get_llm_side_effect

        # This test demonstrates how settings could enable provider fallback
        # (actual implementation would need fallback logic)
        _ = create_settings_snapshot(
            provider="openai",
            overrides={
                "llm.fallback_provider": "anthropic",
                "llm.anthropic.api_key": "fallback-key",
            },
        )

    def test_multi_model_research(self):
        """Test research using multiple models for comparison."""
        models = [
            ("openai", "gpt-4", 0.3),
            ("anthropic", "claude-3-opus-20240229", 0.3),
            ("openai", "gpt-3.5-turbo", 0.5),
        ]

        results = []

        with patch(
            "src.local_deep_research.api.research_functions._init_search_system"
        ) as mock_init:
            mock_system = MagicMock()
            mock_system.analyze_topic.return_value = {
                "current_knowledge": "Model-specific summary",
                "findings": [],
                "iterations": 1,
                "questions": {},
                "formatted_findings": "Findings",
                "all_links_of_system": [],
            }
            mock_init.return_value = mock_system

            for provider, model, temp in models:
                result = quick_summary(
                    "Compare quantum and classical computing",
                    provider=provider,
                    temperature=temp,
                    settings_override={
                        f"llm.{provider}.model": model,
                    },
                )
                results.append((provider, model, result))

            # Verify each call used different settings
            assert mock_init.call_count == len(models)

            for i, (provider, model, _) in enumerate(models):
                call_settings = mock_init.call_args_list[i][1][
                    "settings_snapshot"
                ]
                assert call_settings["llm.provider"]["value"] == provider
                assert call_settings[f"llm.{provider}.model"]["value"] == model


class TestSearchEngineIntegration:
    """Test integration with various search engines through settings."""

    @pytest.mark.skip(
        reason="Requires complex thread context setup - tested via unit tests"
    )
    @patch(
        "src.local_deep_research.web_search_engines.search_engine_factory.get_search"
    )
    def test_search_engine_specific_settings(self, mock_get_search):
        """Test that search engine specific settings are applied."""
        search_configs = [
            {
                "engine": "searxng",
                "settings": {
                    "search.tool": "searxng",
                    "search.engines.searxng.base_url": "https://searx.example.com",
                    "search.engines.searxng.timeout": 15,
                },
            },
            {
                "engine": "duckduckgo",
                "settings": {
                    "search.tool": "duckduckgo",
                    "search.engines.duckduckgo.region": "uk-en",
                    "search.engines.duckduckgo.safe_search": False,
                },
            },
            {
                "engine": "wikipedia",
                "settings": {
                    "search.tool": "wikipedia",
                    "search.engines.wikipedia.language": "es",
                    "search.engines.wikipedia.max_chars": 2000,
                },
            },
        ]

        mock_search = MagicMock()
        mock_search.search.return_value = {"results": []}
        mock_get_search.return_value = mock_search

        with patch(
            "src.local_deep_research.config.llm_config.get_llm"
        ) as mock_llm:
            mock_llm.return_value = MagicMock()

            for config in search_configs:
                _ = quick_summary(
                    f"Test with {config['engine']}",
                    provider="openai",
                    api_key="test-key",
                    settings_override=config["settings"],
                )

                # Verify search engine was configured with correct settings
                last_call = mock_get_search.call_args_list[-1][1]
                assert last_call["search_tool"] == config["engine"]

                # Verify engine-specific settings were passed
                settings_snapshot = last_call.get("settings_snapshot", {})
                for key, value in config["settings"].items():
                    if key in settings_snapshot:
                        assert settings_snapshot[key].get("value") == value


class TestPerformanceSettings:
    """Test performance-related settings."""

    def test_concurrent_research_settings(self):
        """Test settings for concurrent research operations."""
        # Settings optimized for concurrent operations
        concurrent_settings = create_settings_snapshot(
            overrides={
                "research.concurrent.enabled": True,
                "research.concurrent.max_workers": 5,
                "llm.request_timeout": 30,
                "llm.max_retries": 2,
                "search.request_timeout": 10,
                "search.concurrent_searches": 3,
            }
        )

        # Verify concurrent settings
        assert (
            concurrent_settings["research.concurrent.enabled"]["value"] is True
        )
        assert (
            concurrent_settings["research.concurrent.max_workers"]["value"] == 5
        )
        assert concurrent_settings["llm.request_timeout"]["value"] == 30

    def test_rate_limiting_settings(self):
        """Test rate limiting settings for API calls."""
        rate_limit_settings = create_settings_snapshot(
            overrides={
                "llm.rate_limit.enabled": True,
                "llm.rate_limit.requests_per_minute": 60,
                "llm.rate_limit.tokens_per_minute": 90000,
                "llm.rate_limit.concurrent_requests": 5,
                "search.rate_limit.requests_per_second": 10,
            }
        )

        # Verify rate limiting configuration
        assert rate_limit_settings["llm.rate_limit.enabled"]["value"] is True
        assert (
            rate_limit_settings["llm.rate_limit.requests_per_minute"]["value"]
            == 60
        )
        assert (
            rate_limit_settings["search.rate_limit.requests_per_second"][
                "value"
            ]
            == 10
        )


class TestDebugAndMonitoringSettings:
    """Test debug and monitoring related settings."""

    def test_debug_mode_settings(self):
        """Test settings for debug mode."""
        debug_settings = create_settings_snapshot(
            overrides={
                "debug.enabled": True,
                "debug.log_level": "DEBUG",
                "debug.log_api_calls": True,
                "debug.log_search_queries": True,
                "debug.save_intermediate_results": True,
                "debug.pretty_print_responses": True,
            }
        )

        # Verify debug settings
        assert debug_settings["debug.enabled"]["value"] is True
        assert debug_settings["debug.log_level"]["value"] == "DEBUG"
        assert debug_settings["debug.log_api_calls"]["value"] is True

    def test_monitoring_settings(self):
        """Test settings for monitoring and metrics."""
        monitoring_settings = create_settings_snapshot(
            overrides={
                "monitoring.enabled": True,
                "monitoring.metrics.api_latency": True,
                "monitoring.metrics.token_usage": True,
                "monitoring.metrics.search_performance": True,
                "monitoring.export.format": "prometheus",
                "monitoring.export.endpoint": "http://metrics.example.com",
            }
        )

        # Verify monitoring configuration
        assert monitoring_settings["monitoring.enabled"]["value"] is True
        assert (
            monitoring_settings["monitoring.metrics.token_usage"]["value"]
            is True
        )
        assert (
            monitoring_settings["monitoring.export.format"]["value"]
            == "prometheus"
        )
