"""
Comprehensive tests for search_integration module.
Tests NewsSearchCallback, search wrapper, and tracking functionality.
"""

from unittest.mock import patch


class TestNewsSearchCallbackInit:
    """Tests for NewsSearchCallback initialization."""

    def test_creates_instance(self):
        """Test creates instance successfully."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        assert callback is not None

    def test_tracking_enabled_is_none_initially(self):
        """Test _tracking_enabled is None initially."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        assert callback._tracking_enabled is None


class TestTrackingEnabledProperty:
    """Tests for tracking_enabled property."""

    def test_returns_bool(self):
        """Test returns a boolean."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        assert isinstance(callback.tracking_enabled, bool)

    def test_default_is_false(self):
        """Test default value is False."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        assert callback.tracking_enabled is False

    def test_caches_value_after_first_access(self):
        """Test caches value after first access."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        # First access
        _ = callback.tracking_enabled

        # Should be cached now
        assert callback._tracking_enabled is not None


class TestNewsSearchCallbackCall:
    """Tests for NewsSearchCallback __call__ method."""

    def test_accepts_query_and_result(self):
        """Test accepts query and result parameters."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        # Should not raise
        callback("test query", {"findings": []})

    def test_accepts_optional_context(self):
        """Test accepts optional context parameter."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        # Should not raise
        callback("test query", {}, {"user_id": "user123"})

    def test_handles_none_context(self):
        """Test handles None context gracefully."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        # Should not raise
        callback("test query", {}, None)

    def test_defaults_is_user_search_to_true(self):
        """Test defaults is_user_search to True in context."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()
        callback._tracking_enabled = True  # Enable tracking

        with patch.object(callback, "_track_user_search") as mock_track:
            callback("query", {"findings": []}, {})

            mock_track.assert_called_once()

    def test_skips_tracking_when_disabled(self):
        """Test skips tracking when tracking is disabled."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()
        callback._tracking_enabled = False

        with patch.object(callback, "_track_user_search") as mock_track:
            callback("query", {}, {"is_user_search": True})

            mock_track.assert_not_called()

    def test_skips_tracking_for_non_user_search(self):
        """Test skips tracking for non-user searches."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()
        callback._tracking_enabled = True

        with patch.object(callback, "_track_user_search") as mock_track:
            callback("query", {}, {"is_user_search": False})

            mock_track.assert_not_called()


class TestTrackUserSearch:
    """Tests for _track_user_search method."""

    def test_track_user_search_is_method(self):
        """Test _track_user_search is a method."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        assert hasattr(callback, "_track_user_search")
        assert callable(callback._track_user_search)

    def test_track_user_search_accepts_parameters(self):
        """Test _track_user_search accepts required parameters."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )
        import inspect

        callback = NewsSearchCallback()
        sig = inspect.signature(callback._track_user_search)
        params = list(sig.parameters.keys())

        assert "search_id" in params
        assert "user_id" in params
        assert "query" in params
        assert "result" in params

    def test_track_user_search_handles_exceptions(self):
        """Test _track_user_search handles exceptions gracefully."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        # Should not raise even with potential import errors
        # (The actual import happens inside the method)
        callback._track_user_search("id", "user", "query", {})


class TestCalculateQuality:
    """Tests for _calculate_quality method."""

    def test_returns_float(self):
        """Test returns a float."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()
        result = callback._calculate_quality(
            {"findings": [{"content": "test"}]}
        )

        assert isinstance(result, float)

    def test_returns_zero_for_empty_findings(self):
        """Test returns 0.0 for empty findings."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()
        result = callback._calculate_quality({"findings": []})

        assert result == 0.0

    def test_returns_zero_for_no_findings_key(self):
        """Test returns 0.0 when no findings key."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()
        result = callback._calculate_quality({})

        assert result == 0.0

    def test_higher_score_for_more_findings(self):
        """Test returns higher score for more findings."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        few_findings = callback._calculate_quality({"findings": [{}]})
        many_findings = callback._calculate_quality(
            {"findings": [{} for _ in range(10)]}
        )

        assert many_findings >= few_findings

    def test_higher_score_for_findings_with_content(self):
        """Test returns higher score when findings have content."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        without_content = callback._calculate_quality(
            {"findings": [{"title": "test"}]}
        )
        with_content = callback._calculate_quality(
            {"findings": [{"content": "test content"}]}
        )

        assert with_content >= without_content

    def test_score_maxes_at_one(self):
        """Test score is capped at 1.0."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        callback = NewsSearchCallback()

        # Many findings with content
        result = callback._calculate_quality(
            {"findings": [{"content": f"content {i}"} for i in range(100)]}
        )

        assert result <= 1.0


class TestCreateSearchWrapper:
    """Tests for create_search_wrapper function."""

    def test_is_callable(self):
        """Test create_search_wrapper is callable."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )

        assert callable(create_search_wrapper)

    def test_accepts_callable_parameter(self):
        """Test accepts a callable as parameter."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )
        import inspect

        sig = inspect.signature(create_search_wrapper)
        params = list(sig.parameters.keys())

        assert "original_search_method" in params

    def test_returns_function_when_given_function(self):
        """Test returns a function when given a function."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )

        def original(self, query, **kwargs):
            return {}

        result = create_search_wrapper(original)

        assert callable(result)

    def test_preserves_function_name(self):
        """Test preserves original function name."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )

        def my_search_function(self, query, **kwargs):
            """My docstring."""
            return {}

        wrapped = create_search_wrapper(my_search_function)

        assert wrapped.__name__ == "my_search_function"

    def test_preserves_docstring(self):
        """Test preserves original function docstring."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )

        def my_search(self, query, **kwargs):
            """My documentation."""
            return {}

        wrapped = create_search_wrapper(my_search)

        assert wrapped.__doc__ == "My documentation."


class TestSearchWrapperContext:
    """Tests for search wrapper context behavior."""

    def test_wrapper_function_signature(self):
        """Test wrapped function has proper signature."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )

        def original(self, query, **kwargs):
            return {}

        wrapped = create_search_wrapper(original)

        # Should be callable with self, query, and kwargs
        assert callable(wrapped)

    def test_wrapper_extracts_tracking_kwargs(self):
        """Test wrapper extracts tracking-related kwargs."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )

        received_kwargs = {}

        def original(self, query, **kwargs):
            received_kwargs.update(kwargs)
            return {}

        wrapped = create_search_wrapper(original)

        class MockSelf:
            pass

        # These should be extracted and not passed to original
        wrapped(
            MockSelf(),
            "query",
            is_user_search=True,
            is_news_search=False,
            user_id="u1",
        )

        # Original should not receive tracking kwargs
        assert "is_user_search" not in received_kwargs
        assert "is_news_search" not in received_kwargs
        assert "user_id" not in received_kwargs


class TestSearchIntegrationImports:
    """Tests for module imports."""

    def test_imports_news_search_callback(self):
        """Test NewsSearchCallback can be imported."""
        from local_deep_research.news.core.search_integration import (
            NewsSearchCallback,
        )

        assert NewsSearchCallback is not None

    def test_imports_create_search_wrapper(self):
        """Test create_search_wrapper can be imported."""
        from local_deep_research.news.core.search_integration import (
            create_search_wrapper,
        )

        assert create_search_wrapper is not None
        assert callable(create_search_wrapper)
