"""
Tests for the LibraryRAGSearchEngine class.

Tests cover:
- Initialization and configuration
- Search functionality
- Preview generation
- Full content retrieval
"""

from unittest.mock import Mock, patch


class TestLibraryRAGSearchEngineInit:
    """Tests for LibraryRAGSearchEngine initialization."""

    def test_init_with_defaults(self):
        """Initialize with default values."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine()

                assert engine.max_results == 10
                assert engine.username is None
                assert engine.is_local is True

    def test_init_with_settings_snapshot(self):
        """Initialize with settings snapshot."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        settings = {"_username": "testuser"}

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.side_effect = lambda key, default=None, **kwargs: {
                "local_search_embedding_model": "all-MiniLM-L6-v2",
                "local_search_embedding_provider": "sentence_transformers",
                "local_search_chunk_size": 1000,
                "local_search_chunk_overlap": 200,
            }.get(key, default)

            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine(settings_snapshot=settings)

                assert engine.username == "testuser"

    def test_init_with_custom_max_results(self):
        """Initialize with custom max_results."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine(max_results=25)

                assert engine.max_results == 25

    def test_init_with_llm(self):
        """Initialize with LLM."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        mock_llm = Mock()

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine(llm=mock_llm)

                assert engine.llm is mock_llm


class TestSearch:
    """Tests for search method."""

    def test_search_without_username(self):
        """Search returns empty without username."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine()
                results = engine.search("test query")

                assert results == []

    def test_search_no_collections(self):
        """Search handles no collections."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        settings = {"_username": "testuser"}

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                with patch(
                    "local_deep_research.web_search_engines.engines.search_engine_library.LibraryService"
                ) as mock_service:
                    mock_service.return_value.get_all_collections.return_value = []

                    engine = LibraryRAGSearchEngine(settings_snapshot=settings)
                    results = engine.search("test query")

                    assert results == []

    def test_search_returns_results(self):
        """Search returns formatted results."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        settings = {"_username": "testuser"}

        mock_doc = Mock()
        mock_doc.page_content = "This is the document content for testing."
        mock_doc.metadata = {
            "source_id": "123",
            "document_title": "Test Document",
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                with patch(
                    "local_deep_research.web_search_engines.engines.search_engine_library.LibraryService"
                ) as mock_lib_service:
                    mock_lib_service.return_value.get_all_collections.return_value = [
                        {"id": 1, "name": "Test Collection"}
                    ]

                    with patch(
                        "local_deep_research.web_search_engines.engines.search_engine_library.get_user_db_session"
                    ) as mock_session:
                        mock_rag_index = Mock()
                        mock_rag_index.embedding_model = "all-MiniLM-L6-v2"
                        mock_rag_index.embedding_model_type = Mock(
                            value="sentence_transformers"
                        )
                        mock_rag_index.chunk_size = 1000
                        mock_rag_index.chunk_overlap = 200

                        mock_session.return_value.__enter__.return_value.query.return_value.filter_by.return_value.first.return_value = mock_rag_index

                        with patch(
                            "local_deep_research.web_search_engines.engines.search_engine_library.LibraryRAGService"
                        ) as mock_rag_service:
                            # Set up mock RAG service instance for context manager
                            mock_rag_instance = Mock()
                            mock_rag_instance.get_rag_stats.return_value = {
                                "indexed_documents": 1
                            }
                            mock_vector_store = Mock()
                            mock_vector_store.similarity_search_with_score.return_value = [
                                (mock_doc, 0.5)
                            ]
                            mock_rag_instance.load_or_create_faiss_index.return_value = mock_vector_store
                            # Configure context manager behavior
                            mock_rag_service.return_value.__enter__.return_value = mock_rag_instance
                            mock_rag_service.return_value.__exit__.return_value = None

                            engine = LibraryRAGSearchEngine(
                                settings_snapshot=settings
                            )
                            results = engine.search("test query")

                            assert len(results) == 1
                            assert results[0]["title"] == "Test Document"
                            assert results[0]["source"] == "library"
                            assert results[0]["source_type"] == "library"
                            assert "/library/document/123" in results[0]["url"]

    def test_search_exception(self):
        """Search re-raises so failures are not mistaken for no results."""
        import pytest

        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        settings = {"_username": "testuser"}

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                with patch(
                    "local_deep_research.web_search_engines.engines.search_engine_library.LibraryService"
                ) as mock_service:
                    mock_service.return_value.get_all_collections.side_effect = Exception(
                        "Service error"
                    )

                    engine = LibraryRAGSearchEngine(settings_snapshot=settings)
                    with pytest.raises(Exception, match="Service error"):
                        engine.search("test query")


class TestGetPreviews:
    """Tests for _get_previews method."""

    def test_get_previews_delegates_to_search(self):
        """Get previews delegates to search method."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine()

                with patch.object(
                    engine, "search", return_value=[{"title": "Test"}]
                ) as mock_search:
                    results = engine._get_previews("test query", limit=5)

                    mock_search.assert_called_once()
                    assert results == [{"title": "Test"}]

    def test_get_previews_uses_max_results_when_no_limit(self):
        """Without an explicit limit, the configured max_results is used.

        Regression test for #4428: the base class run() calls
        _get_previews(query) without a limit, so the configured
        search.max_results must be forwarded to search() instead of a
        hardcoded default.
        """
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine(max_results=30)

                with patch.object(
                    engine, "search", return_value=[]
                ) as mock_search:
                    engine._get_previews("test query")

                    mock_search.assert_called_once_with(
                        "test query", 30, None, None
                    )


# Note: _get_full_content tests are skipped due to a bug in the source code
# where `from ... import search_config` doesn't work (search_config is in
# the config subpackage, not the root package). The method would need to
# use `from ...config import search_config` instead.


class TestClose:
    """Tests for close method."""

    def test_close_does_nothing(self):
        """Close method runs without error."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_library.get_setting_from_snapshot",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_library.get_server_url",
                return_value="http://localhost:5000",
            ):
                engine = LibraryRAGSearchEngine()
                engine.close()  # Should not raise


class TestClassAttributes:
    """Tests for class attributes."""

    def test_is_local(self):
        """LibraryRAGSearchEngine is marked as local."""
        from local_deep_research.web_search_engines.engines.search_engine_library import (
            LibraryRAGSearchEngine,
        )

        assert LibraryRAGSearchEngine.is_local is True
