"""
Additional tests for MCP server to increase coverage.

Tests for:
- Falsy value handling in _build_settings_overrides
- Discovery tools error paths
- Entry point functions
- Temperature parameter in research tools
- Import smoke tests
- formatted_findings field
"""

from unittest.mock import patch

import pytest

# Skip all tests if MCP is not available
try:
    import mcp  # noqa: F401

    MCP_AVAILABLE = True
except ImportError:
    MCP_AVAILABLE = False

pytestmark = pytest.mark.skipif(
    not MCP_AVAILABLE, reason="MCP package not installed"
)


class TestBuildSettingsOverridesFalsyValues:
    """Tests for falsy value handling in _build_settings_overrides.

    Note: The implementation uses 'is not None' checks, so falsy values
    like 0, 0.0, and "" ARE included in the result (they are valid values).
    """

    def test_iterations_zero_is_added(self):
        """Test that iterations=0 IS added (is not None check)."""
        from local_deep_research.mcp.server import _build_settings_overrides

        # Implementation uses 'is not None', so 0 is added
        result = _build_settings_overrides(iterations=0)
        assert result.get("search.iterations") == 0

    def test_questions_per_iteration_zero_is_added(self):
        """Test that questions_per_iteration=0 IS added."""
        from local_deep_research.mcp.server import _build_settings_overrides

        result = _build_settings_overrides(questions_per_iteration=0)
        assert result.get("search.questions_per_iteration") == 0

    def test_temperature_zero_is_added(self):
        """Test that temperature=0.0 IS added (is not None check)."""
        from local_deep_research.mcp.server import _build_settings_overrides

        result = _build_settings_overrides(temperature=0.0)
        assert result.get("llm.temperature") == 0.0

    def test_empty_string_search_engine_is_stripped(self):
        """Test that empty string search_engine is stripped and not added."""
        from local_deep_research.mcp.server import _build_settings_overrides

        result = _build_settings_overrides(search_engine="")
        assert result == {}

    def test_empty_string_strategy_is_stripped(self):
        """Test that empty string strategy is stripped and not added."""
        from local_deep_research.mcp.server import _build_settings_overrides

        result = _build_settings_overrides(strategy="")
        assert result == {}

    def test_positive_temperature_added(self):
        """Test that positive temperature is correctly added."""
        from local_deep_research.mcp.server import _build_settings_overrides

        result = _build_settings_overrides(temperature=0.5)
        assert result["llm.temperature"] == 0.5

    def test_temperature_one_added(self):
        """Test that temperature=1.0 is correctly added."""
        from local_deep_research.mcp.server import _build_settings_overrides

        result = _build_settings_overrides(temperature=1.0)
        assert result["llm.temperature"] == 1.0


class TestDiscoveryToolsErrorPaths:
    """Tests for error handling in discovery tools."""

    def test_list_search_engines_handles_import_error(self):
        """Test list_search_engines handles import errors gracefully."""
        from local_deep_research.mcp.server import list_search_engines

        # The function imports create_settings_snapshot inside, so we need to patch it there
        with patch(
            "local_deep_research.api.settings_utils.create_settings_snapshot",
            side_effect=ImportError("Module not found"),
        ):
            result = list_search_engines()

        assert result["status"] == "error"
        assert result["error_type"] == "model_not_found"

    def test_list_search_engines_handles_config_error(self):
        """Test list_search_engines handles config errors gracefully."""
        from local_deep_research.mcp.server import list_search_engines

        with patch(
            "local_deep_research.web_search_engines.search_engines_config.search_config",
            side_effect=Exception("Config load failed"),
        ):
            result = list_search_engines()

        assert result["status"] == "error"
        assert result["error_type"] == "unknown"

    def test_get_configuration_handles_settings_error(self):
        """Test get_configuration handles settings errors gracefully."""
        from local_deep_research.mcp.server import get_configuration

        # Patch at the location where it's imported inside the function
        with patch(
            "local_deep_research.api.settings_utils.create_settings_snapshot",
            side_effect=Exception("Settings unavailable"),
        ):
            result = get_configuration()

        assert result["status"] == "error"
        assert result["error_type"] == "service_unavailable"

    def test_get_configuration_handles_extract_error(self):
        """Test get_configuration handles extraction errors gracefully."""
        from local_deep_research.mcp.server import get_configuration

        # Patch at the location where it's imported inside the function
        with patch(
            "local_deep_research.api.settings_utils.extract_setting_value",
            side_effect=Exception("Extract failed"),
        ):
            result = get_configuration()

        assert result["status"] == "error"
        assert result["error_type"] == "unknown"


class TestEntryPoints:
    """Tests for entry point functions."""

    def test_run_server_function_is_callable(self):
        """Test that run_server is a callable function."""
        from local_deep_research.mcp.server import run_server

        assert callable(run_server)

    def test_run_server_exported_from_package(self):
        """Test that run_server is exported from the mcp package."""
        from local_deep_research.mcp import run_server

        assert callable(run_server)

    def test_mcp_instance_exported_from_package(self):
        """Test that mcp instance is exported from the mcp package."""
        from local_deep_research.mcp import mcp

        assert mcp is not None
        assert mcp.name == "local-deep-research"

    def test_main_module_imports_run_server(self):
        """Test that __main__.py can import run_server."""
        # This tests that the import in __main__.py works
        from local_deep_research.mcp.__main__ import run_server

        assert callable(run_server)

    def test_run_server_disables_loguru_diagnose(self):
        """The MCP stderr sink must pin diagnose=False (issue #4185).

        loguru defaults diagnose=True, which dumps repr() of every traceback
        frame's locals on exception. The MCP server calls logger.exception()
        in many request handlers whose frame locals hold credentials
        (api_key, Authorization headers, search-engine secrets); leaving the
        default on would write them to the MCP client's stderr log.
        """
        import local_deep_research.mcp.server as server_module

        with patch.object(server_module, "logger") as mock_logger:
            with patch.object(server_module.mcp, "run"):
                server_module.run_server()

        assert mock_logger.add.call_args_list, "stderr sink was not added"
        for add_call in mock_logger.add.call_args_list:
            assert add_call.kwargs.get("diagnose") is False, (
                f"MCP logger.add must pass diagnose=False; got "
                f"{add_call.kwargs.get('diagnose')!r}"
            )


class TestTemperatureParameter:
    """Tests for temperature parameter in research tools."""

    def test_quick_research_with_temperature(self):
        """Test quick_research passes temperature override."""
        from local_deep_research.mcp.server import quick_research

        with patch(
            "local_deep_research.mcp.server.ldr_quick_summary",
            return_value={
                "summary": "Test",
                "findings": [],
                "sources": [],
                "iterations": 1,
            },
        ):
            with patch(
                "local_deep_research.mcp.server.create_settings_snapshot"
            ) as mock_settings:
                mock_settings.return_value = {}
                # Note: quick_research doesn't have temperature param currently
                # This test documents the current behavior
                result = quick_research(query="test", iterations=2)

        assert result["status"] == "success"

    def test_detailed_research_with_temperature(self):
        """Test detailed_research passes temperature override."""
        from local_deep_research.mcp.server import detailed_research

        with patch(
            "local_deep_research.mcp.server.ldr_detailed_research",
            return_value={
                "query": "test",
                "research_id": "123",
                "summary": "Test",
                "findings": [],
                "sources": [],
                "iterations": 1,
            },
        ):
            with patch(
                "local_deep_research.mcp.server.create_settings_snapshot"
            ) as mock_settings:
                mock_settings.return_value = {}
                result = detailed_research(query="test", iterations=2)

        assert result["status"] == "success"


class TestFormattedFindings:
    """Tests for formatted_findings field in research results."""

    def test_quick_research_returns_formatted_findings(self):
        """Test quick_research returns formatted_findings field."""
        from local_deep_research.mcp.server import quick_research

        with patch(
            "local_deep_research.mcp.server.ldr_quick_summary",
            return_value={
                "summary": "Test summary",
                "findings": [],
                "sources": [],
                "iterations": 1,
                "formatted_findings": "## Formatted Content\n\nTest formatted findings",
            },
        ):
            result = quick_research(query="test")

        assert result["status"] == "success"
        assert "formatted_findings" in result
        assert (
            result["formatted_findings"]
            == "## Formatted Content\n\nTest formatted findings"
        )

    def test_quick_research_formatted_findings_defaults_empty(self):
        """Test formatted_findings defaults to empty string when missing."""
        from local_deep_research.mcp.server import quick_research

        with patch(
            "local_deep_research.mcp.server.ldr_quick_summary",
            return_value={
                "summary": "Test",
                "findings": [],
                "sources": [],
                "iterations": 1,
                # No formatted_findings
            },
        ):
            result = quick_research(query="test")

        assert result["status"] == "success"
        assert result["formatted_findings"] == ""

    def test_detailed_research_returns_formatted_findings(self):
        """Test detailed_research returns formatted_findings field."""
        from local_deep_research.mcp.server import detailed_research

        with patch(
            "local_deep_research.mcp.server.ldr_detailed_research",
            return_value={
                "query": "test",
                "research_id": "123",
                "summary": "Test",
                "findings": [],
                "sources": [],
                "iterations": 1,
                "formatted_findings": "## Detailed Formatted\n\nContent here",
            },
        ):
            result = detailed_research(query="test")

        assert result["status"] == "success"
        assert "formatted_findings" in result
        assert "Detailed Formatted" in result["formatted_findings"]


class TestImportSmokeTests:
    """Smoke tests to verify module imports work correctly."""

    def test_server_module_imports(self):
        """Test that server module imports without errors."""
        from local_deep_research.mcp import server

        assert hasattr(server, "mcp")
        assert hasattr(server, "run_server")
        assert hasattr(server, "quick_research")
        assert hasattr(server, "detailed_research")
        assert hasattr(server, "generate_report")
        assert hasattr(server, "analyze_documents")
        assert hasattr(server, "list_search_engines")
        assert hasattr(server, "list_strategies")
        assert hasattr(server, "get_configuration")

    def test_all_tools_have_type_hints(self):
        """Test that all tool functions have return type hints."""
        from local_deep_research.mcp.server import (
            quick_research,
            detailed_research,
            generate_report,
            analyze_documents,
            list_search_engines,
            list_strategies,
            get_configuration,
        )
        import typing

        for func in [
            quick_research,
            detailed_research,
            generate_report,
            analyze_documents,
            list_search_engines,
            list_strategies,
            get_configuration,
        ]:
            hints = typing.get_type_hints(func)
            assert "return" in hints, (
                f"{func.__name__} missing return type hint"
            )

    def test_helper_functions_exist(self):
        """Test that helper functions exist and are importable."""
        from local_deep_research.mcp.server import (
            _classify_error,
            _build_settings_overrides,
        )
        from local_deep_research.search_system_factory import (
            get_available_strategies,
        )

        assert callable(_classify_error)
        assert callable(_build_settings_overrides)
        strategies = get_available_strategies()
        assert isinstance(strategies, list)


class TestAvailableStrategiesContent:
    """Tests for get_available_strategies content validation."""

    def test_all_strategies_have_unique_names(self):
        """Test that all strategy names are unique."""
        from local_deep_research.search_system_factory import (
            get_available_strategies,
        )

        strategies = get_available_strategies()
        names = [s["name"] for s in strategies]
        assert len(names) == len(set(names)), "Duplicate strategy names found"

    def test_all_strategies_have_non_empty_descriptions(self):
        """Test that all strategies have meaningful descriptions."""
        from local_deep_research.search_system_factory import (
            get_available_strategies,
        )

        strategies = get_available_strategies()
        for strategy in strategies:
            assert len(strategy["description"]) > 10, (
                f"Strategy {strategy['name']} has too short description"
            )

    def test_key_strategies_included(self):
        """Test that key strategies are included in the full list."""
        from local_deep_research.search_system_factory import (
            get_available_strategies,
        )

        strategies = get_available_strategies()
        names = [s["name"] for s in strategies]

        # These are the actual strategies defined in the system
        expected_strategies = [
            "source-based",
            "focused-iteration",
            "focused-iteration-standard",
            "topic-organization",
            "langgraph-agent",
        ]

        for expected in expected_strategies:
            assert expected in names, (
                f"Expected strategy '{expected}' not found"
            )


class TestErrorClassificationAdditional:
    """Additional error classification tests."""

    def test_classify_error_case_insensitive(self):
        """Test that error classification is case insensitive."""
        from local_deep_research.mcp.server import _classify_error

        assert _classify_error("SERVICE UNAVAILABLE") == "service_unavailable"
        assert _classify_error("TIMEOUT error") == "timeout"
        assert _classify_error("API KEY invalid") == "auth_error"

    def test_classify_error_partial_match(self):
        """Test that error classification works with partial matches."""
        from local_deep_research.mcp.server import _classify_error

        assert (
            _classify_error("The authentication system rejected the request")
            == "auth_error"
        )
        # The word "timeout" must be present for timeout classification
        assert _classify_error("Request timeout after 30 seconds") == "timeout"
        assert (
            _classify_error("Connection refused by server")
            == "connection_error"
        )

    def test_classify_error_empty_string(self):
        """Test classification of empty error string."""
        from local_deep_research.mcp.server import _classify_error

        assert _classify_error("") == "unknown"

    def test_classify_error_none_like(self):
        """Test classification handles various error strings."""
        from local_deep_research.mcp.server import _classify_error

        # All unknown errors
        assert _classify_error("Something went wrong") == "unknown"
        assert _classify_error("Error occurred") == "unknown"
        assert _classify_error("null pointer exception") == "unknown"


class TestListSearchEnginesOutput:
    """Tests for list_search_engines output structure."""

    def test_engines_sorted_by_name(self):
        """Test that engines are sorted alphabetically by name."""
        from local_deep_research.mcp.server import list_search_engines

        mock_engines = {
            "z_engine": {"description": "Z engine"},
            "a_engine": {"description": "A engine"},
            "m_engine": {"description": "M engine"},
        }

        with patch(
            "local_deep_research.web_search_engines.search_engines_config.search_config",
            return_value=mock_engines,
        ):
            result = list_search_engines()

        assert result["status"] == "success"
        names = [e["name"] for e in result["engines"]]
        assert names == sorted(names), "Engines should be sorted alphabetically"

    def test_engine_info_structure(self):
        """Test that each engine has the expected info structure."""
        from local_deep_research.mcp.server import list_search_engines

        mock_engines = {
            "test_engine": {
                "description": "Test engine",
                "strengths": ["Fast", "Free"],
                "weaknesses": ["Limited"],
                "requires_api_key": True,
                "is_local": False,
            }
        }

        with patch(
            "local_deep_research.web_search_engines.search_engines_config.search_config",
            return_value=mock_engines,
        ):
            result = list_search_engines()

        assert result["status"] == "success"
        engine = result["engines"][0]
        assert engine["name"] == "test_engine"
        assert engine["description"] == "Test engine"
        assert engine["strengths"] == ["Fast", "Free"]
        assert engine["weaknesses"] == ["Limited"]
        assert engine["requires_api_key"] is True
        assert engine["is_local"] is False

    def test_engine_info_defaults(self):
        """Test that engine info defaults work for missing fields."""
        from local_deep_research.mcp.server import list_search_engines

        mock_engines = {
            "minimal_engine": {}  # No fields
        }

        with patch(
            "local_deep_research.web_search_engines.search_engines_config.search_config",
            return_value=mock_engines,
        ):
            result = list_search_engines()

        assert result["status"] == "success"
        engine = result["engines"][0]
        assert engine["name"] == "minimal_engine"
        assert engine["description"] == ""
        assert engine["strengths"] == []
        assert engine["weaknesses"] == []
        assert engine["requires_api_key"] is False
        assert engine["is_local"] is False
