"""
Branch-coverage gap tests for mcp/client.py.

Targets uncovered paths that existing tests miss:
- Init retry loop exhausting on asyncio.TimeoutError → MCPClientError with
  "Timeout initializing" (lines 224-228)
- Init retry loop exhausting on an exception whose message contains
  "before initialization" → MCPClientError (lines 231-236)
- call_tool with a non-iterable, non-None content value — goes through the
  elif branch at line 325-327 and stringifies the value. The existing
  test_mcp_call_tool_parsing.py tests a LOCAL copy of this logic; this
  exercises the real MCPClient.call_tool().
"""

import asyncio
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

MODULE = "local_deep_research.mcp.client"


def _mcp_available():
    try:
        import mcp  # noqa: F401

        return True
    except ImportError:
        return False


pytestmark = pytest.mark.skipif(
    not _mcp_available(),
    reason="mcp package not installed",
)


def _valid_config(command="python", name="test-server"):
    return {"command": command, "args": ["server.py"], "name": name}


def _make_client():
    from local_deep_research.mcp.client import MCPClient

    return MCPClient(_valid_config())


def _make_connected_client():
    client = _make_client()
    client._connected = True
    client._session = MagicMock()
    return client


class TestInitializeWithRetry:
    """Retry loop exhaustion paths for _initialize_with_retry."""

    @patch(f"{MODULE}.INIT_RETRY_ATTEMPTS", 2)
    @patch(f"{MODULE}.INIT_RETRY_BASE_DELAY", 0.01)
    @patch(f"{MODULE}.INIT_RETRY_MAX_DELAY", 0.01)
    def test_timeout_exhaustion_raises_mcp_timeout_error(self):
        from local_deep_research.mcp.client import MCPClientError

        client = _make_client()
        client._session = MagicMock()
        client._session.initialize = AsyncMock(
            side_effect=asyncio.TimeoutError()
        )
        client.timeout = 0.1

        with pytest.raises(MCPClientError, match="Timeout initializing"):
            asyncio.run(client._initialize_with_retry())

    @patch(f"{MODULE}.INIT_RETRY_ATTEMPTS", 2)
    @patch(f"{MODULE}.INIT_RETRY_BASE_DELAY", 0.01)
    @patch(f"{MODULE}.INIT_RETRY_MAX_DELAY", 0.01)
    def test_before_initialization_exhaustion_raises_mcp_client_error(self):
        from local_deep_research.mcp.client import MCPClientError

        client = _make_client()
        client._session = MagicMock()
        client._session.initialize = AsyncMock(
            side_effect=RuntimeError(
                "Received request before initialization was complete"
            )
        )
        client.timeout = 0.1

        with pytest.raises(MCPClientError, match="Failed to initialize"):
            asyncio.run(client._initialize_with_retry())


class TestCallToolNonIterableContent:
    """call_tool stringifies non-None content that lacks __iter__."""

    def test_integer_content_stringified(self):
        client = _make_connected_client()

        mock_result = MagicMock()
        mock_result.isError = False
        # Plain int — not None, no __iter__ — must hit the elif branch.
        mock_result.content = 42
        client._session.call_tool = AsyncMock(return_value=mock_result)

        result = asyncio.run(client.call_tool("numeric_tool"))

        assert result["status"] == "success"
        assert result["content"] == "42"
        assert result["raw"] == 42
