"""
Coverage tests for local_deep_research/utilities/llm_utils.py.

Focuses on paths not exercised by existing test files:
- _close_base_llm: close() delegation, Ollama httpx client closing,
  non-Ollama _client skip, missing _client attr, missing httpx client
- fetch_ollama_models: verifies safe_get (not requests.get) is used
"""

from unittest.mock import Mock, patch

from local_deep_research.utilities.llm_utils import (
    _close_base_llm,
    fetch_ollama_models,
)


# ---------------------------------------------------------------------------
# _close_base_llm
# ---------------------------------------------------------------------------


class TestCloseBaseLlmDelegation:
    """When the LLM object has its own close() method defined on the type."""

    def test_delegates_to_close_method(self):
        """Should call llm.close() when the type defines close."""

        class ClosableLLM:
            def close(self):
                pass

        llm = ClosableLLM()
        llm.close = Mock()
        _close_base_llm(llm)
        llm.close.assert_called_once()

    def test_does_not_inspect_client_after_close(self):
        """After calling close(), should return immediately without
        inspecting _client."""

        class ClosableLLM:
            def close(self):
                pass

        llm = ClosableLLM()
        llm.close = Mock()
        llm._client = Mock()
        _close_base_llm(llm)
        llm.close.assert_called_once()
        llm._client.close.assert_not_called()


class TestCloseBaseLlmOllamaClient:
    """When the LLM has an Ollama-module _client with an httpx _client."""

    def test_closes_httpx_client_on_ollama(self):
        """Should close the nested httpx client on an Ollama _client."""
        httpx_client = Mock()
        httpx_client.close = Mock()

        ollama_client = Mock()
        ollama_client._client = httpx_client
        type(ollama_client).__module__ = "ollama._client"

        llm = Mock(spec=[])
        llm._client = ollama_client

        _close_base_llm(llm)
        httpx_client.close.assert_called_once()

    def test_skips_non_ollama_client_module(self):
        """Should NOT close _client._client when module is not ollama."""
        httpx_client = Mock()
        httpx_client.close = Mock()

        non_ollama_client = Mock()
        non_ollama_client._client = httpx_client
        type(non_ollama_client).__module__ = "openai._http_client"

        llm = Mock(spec=[])
        llm._client = non_ollama_client

        _close_base_llm(llm)
        httpx_client.close.assert_not_called()

    def test_no_client_attr_is_noop(self):
        """Should do nothing when llm has no _client attribute."""
        llm = Mock(spec=[])
        assert (
            not hasattr(llm, "_client") or getattr(llm, "_client", None) is None
        )
        _close_base_llm(llm)

    def test_ollama_client_without_httpx_client(self):
        """Should handle Ollama _client that has no nested _client (httpx)."""
        ollama_client = Mock(spec=[])
        type(ollama_client).__module__ = "ollama._client"

        llm = Mock(spec=[])
        llm._client = ollama_client
        _close_base_llm(llm)

    def test_ollama_httpx_client_without_close(self):
        """Should handle Ollama httpx _client that lacks close()."""
        httpx_client = Mock(spec=[])

        ollama_client = Mock()
        ollama_client._client = httpx_client
        type(ollama_client).__module__ = "ollama.core"

        llm = Mock(spec=[])
        llm._client = ollama_client
        _close_base_llm(llm)


# ---------------------------------------------------------------------------
# fetch_ollama_models - verifies safe_get usage
# ---------------------------------------------------------------------------


class TestFetchOllamaModelsUsesSafeGet:
    """Verify that fetch_ollama_models calls safe_get, not requests.get."""

    def test_calls_safe_get_not_requests_get(self):
        """safe_get should be called; requests.get should NOT."""
        mock_safe_get = Mock()
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"models": [{"name": "llama3"}]}
        mock_safe_get.return_value = mock_response

        with patch(
            "local_deep_research.utilities.llm_utils.safe_get",
            mock_safe_get,
            create=True,
        ):
            with patch("local_deep_research.security.safe_get", mock_safe_get):
                result = fetch_ollama_models("http://localhost:11434")

                mock_safe_get.assert_called()
                call_args = mock_safe_get.call_args
                assert "localhost:11434/api/tags" in call_args.args[0]
                assert call_args.kwargs["allow_localhost"] is True
                assert call_args.kwargs["allow_private_ips"] is True

        assert len(result) == 1
        assert result[0]["value"] == "llama3"

    def test_safe_get_exception_returns_empty_list(self):
        """When safe_get raises, should return [] gracefully."""
        with patch(
            "local_deep_research.security.safe_get",
            side_effect=RuntimeError("SSRF blocked"),
        ):
            result = fetch_ollama_models("http://evil.com:11434")
            assert result == []

    def test_safe_get_non_200_returns_empty(self):
        """Non-200 response from safe_get should yield empty list."""
        mock_response = Mock()
        mock_response.status_code = 503

        with patch(
            "local_deep_research.security.safe_get",
            return_value=mock_response,
        ):
            result = fetch_ollama_models("http://localhost:11434")
            assert result == []
