"""Tests for _create_full_search_wrapper in search_engine_factory.py.

The entire _create_full_search_wrapper function (~160 lines) was never called
in any test. These tests exercise the wrapper creation logic, parameter
filtering, and API key extraction from settings snapshots.
"""

from unittest.mock import Mock, patch

import pytest

from local_deep_research.web_search_engines.search_engine_factory import (
    _create_full_search_wrapper,
)


@pytest.fixture
def base_engine():
    """Provide a mock base engine."""
    return Mock()


@pytest.fixture
def mock_llm():
    """Provide a mock LLM."""
    return Mock()


class TestWrapperCreationConditions:
    """Tests for when the wrapper is and isn't created."""

    def test_wrapper_returns_base_engine_when_config_not_found(
        self, base_engine, mock_llm
    ):
        """Missing engine config -> returns base engine unchanged."""
        settings_snapshot = {
            # No search.engine.web.* keys for our engine
            "search.other_setting": {"value": "something"},
        }

        result = _create_full_search_wrapper(
            "nonexistent_engine",
            base_engine,
            mock_llm,
            {},
            settings_snapshot=settings_snapshot,
        )

        assert result is base_engine

    def test_wrapper_returns_base_engine_on_exception(
        self, base_engine, mock_llm
    ):
        """Exception in wrapper creation -> fallback to base engine."""
        settings_snapshot = {
            "search.engine.web.test_engine.full_search_module": {
                "value": "nonexistent.module"
            },
            "search.engine.web.test_engine.full_search_class": {
                "value": "NonexistentClass"
            },
        }

        with patch(
            "local_deep_research.web_search_engines.search_engine_factory.get_safe_module_class",
            side_effect=ImportError("module not found"),
        ):
            result = _create_full_search_wrapper(
                "test_engine",
                base_engine,
                mock_llm,
                {},
                settings_snapshot=settings_snapshot,
            )

        assert result is base_engine

    def test_wrapper_returns_base_when_no_full_search_config(
        self, base_engine, mock_llm
    ):
        """Missing full_search_module/class in config -> returns base engine."""
        settings_snapshot = {
            "search.engine.web.test_engine.class_name": {"value": "TestEngine"},
            # No full_search_module or full_search_class
        }

        result = _create_full_search_wrapper(
            "test_engine",
            base_engine,
            mock_llm,
            {},
            settings_snapshot=settings_snapshot,
        )

        assert result is base_engine


class _FakeWrapperAcceptsAll:
    """Fake wrapper class that accepts specific init params."""

    def __init__(self, llm=None, web_search=None):
        self.llm = llm
        self.web_search = web_search


class _FakeWrapperWithApiKey:
    """Fake wrapper class that accepts api_key and llm."""

    def __init__(self, api_key=None, llm=None):
        self.api_key = api_key
        self.llm = llm


class TestWrapperParameterFiltering:
    """Tests for parameter filtering during wrapper creation."""

    def test_wrapper_filters_init_params(self, base_engine, mock_llm):
        """Only params accepted by wrapper __init__ are passed."""
        settings_snapshot = {
            "search.engine.web.test_engine.full_search_module": {
                "value": "some.module"
            },
            "search.engine.web.test_engine.full_search_class": {
                "value": "SomeClass"
            },
        }

        params = {
            "llm": mock_llm,
            "web_search": base_engine,
            "unsupported_param": "should_be_filtered",
            "another_unsupported": 42,
        }

        with patch(
            "local_deep_research.web_search_engines.search_engine_factory.get_safe_module_class",
            return_value=_FakeWrapperAcceptsAll,
        ):
            result = _create_full_search_wrapper(
                "test_engine",
                base_engine,
                mock_llm,
                params,
                settings_snapshot=settings_snapshot,
            )

        assert isinstance(result, _FakeWrapperAcceptsAll)
        assert result.llm is mock_llm
        assert result.web_search is base_engine


class TestApiKeyExtraction:
    """Tests for API key extraction from settings snapshots."""

    def test_api_key_from_settings_snapshot_dict_format(
        self, base_engine, mock_llm
    ):
        """API key extraction from {"value": "key"} format in settings."""
        settings_snapshot = {
            "search.engine.web.brave.full_search_module": {
                "value": "some.module"
            },
            "search.engine.web.brave.full_search_class": {
                "value": "BraveSearch"
            },
            "search.engine.web.brave.api_key": {"value": "test-api-key-123"},
        }

        with patch(
            "local_deep_research.web_search_engines.search_engine_factory.get_safe_module_class",
            return_value=_FakeWrapperWithApiKey,
        ):
            result = _create_full_search_wrapper(
                "brave",
                base_engine,
                mock_llm,
                {},
                settings_snapshot=settings_snapshot,
            )

        assert isinstance(result, _FakeWrapperWithApiKey)
        assert result.api_key == "test-api-key-123"

    def test_api_key_from_settings_snapshot_plain_format(
        self, base_engine, mock_llm
    ):
        """API key extraction from plain string format in settings."""
        settings_snapshot = {
            "search.engine.web.brave.full_search_module": {
                "value": "some.module"
            },
            "search.engine.web.brave.full_search_class": {
                "value": "BraveSearch"
            },
            "search.engine.web.brave.api_key": "plain-api-key-456",
        }

        with patch(
            "local_deep_research.web_search_engines.search_engine_factory.get_safe_module_class",
            return_value=_FakeWrapperWithApiKey,
        ):
            result = _create_full_search_wrapper(
                "brave",
                base_engine,
                mock_llm,
                {},
                settings_snapshot=settings_snapshot,
            )

        assert isinstance(result, _FakeWrapperWithApiKey)
        assert result.api_key == "plain-api-key-456"

    def test_api_key_fallback_to_engine_config(self, base_engine, mock_llm):
        """When settings key is missing, wrapper still works — API key
        may come from other sources or be optional."""
        settings_snapshot = {
            "search.engine.web.brave.full_search_module": {
                "value": "some.module"
            },
            "search.engine.web.brave.full_search_class": {
                "value": "BraveSearch"
            },
            # No api_key in settings
        }

        with patch(
            "local_deep_research.web_search_engines.search_engine_factory.get_safe_module_class",
            return_value=_FakeWrapperWithApiKey,
        ):
            result = _create_full_search_wrapper(
                "brave",
                base_engine,
                mock_llm,
                {},
                settings_snapshot=settings_snapshot,
            )

        assert isinstance(result, _FakeWrapperWithApiKey)
        # api_key should be None since it's not in settings
        assert result.api_key is None

    def test_wrapper_with_no_settings_snapshot(self, base_engine, mock_llm):
        """When settings_snapshot is None, it attempts search_config fallback
        which may return empty config and return base engine."""
        with patch(
            "local_deep_research.web_search_engines.search_engine_factory.search_config",
            return_value={},
        ):
            result = _create_full_search_wrapper(
                "test_engine",
                base_engine,
                mock_llm,
                {},
                settings_snapshot=None,
            )

        # Engine not found in empty config -> returns base engine
        assert result is base_engine
