"""
Comprehensive tests for the Retriever search engine (LangChain wrapper).
Tests initialization, search functionality, and document conversion.

Note: These tests mock LangChain retrievers to avoid requiring actual vector stores.
"""

import pytest
from unittest.mock import Mock, AsyncMock


class MockDocument:
    """Mock LangChain Document."""

    def __init__(self, page_content: str, metadata: dict = None):
        self.page_content = page_content
        self.metadata = metadata or {}


class TestRetrieverSearchEngineInit:
    """Tests for Retriever search engine initialization."""

    def test_init_with_retriever(self):
        """Test initialization with a retriever."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "MockRetriever"

        engine = RetrieverSearchEngine(retriever=mock_retriever)

        assert engine.retriever == mock_retriever
        assert engine.max_results == 10
        assert engine.name == "MockRetriever"

    def test_init_with_custom_max_results(self):
        """Test initialization with custom max results."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "MockRetriever"

        engine = RetrieverSearchEngine(retriever=mock_retriever, max_results=25)

        assert engine.max_results == 25

    def test_init_with_custom_name(self):
        """Test initialization with custom name."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "MockRetriever"

        engine = RetrieverSearchEngine(
            retriever=mock_retriever, name="MyCustomRetriever"
        )

        assert engine.name == "MyCustomRetriever"

    def test_init_uses_class_name_when_no_name_provided(self):
        """Test that engine uses retriever class name when no name provided."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "FAISSRetriever"

        engine = RetrieverSearchEngine(retriever=mock_retriever)

        assert engine.name == "FAISSRetriever"


class TestRetrieverSearchExecution:
    """Tests for Retriever search execution."""

    @pytest.fixture
    def mock_retriever(self):
        """Create a mock retriever."""
        retriever = Mock()
        retriever.__class__.__name__ = "MockRetriever"
        return retriever

    @pytest.fixture
    def engine(self, mock_retriever):
        """Create a Retriever engine with mock retriever."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        return RetrieverSearchEngine(retriever=mock_retriever, max_results=10)

    def test_run_success(self, engine):
        """Test successful search execution."""
        engine.retriever.invoke.return_value = [
            MockDocument(
                "Machine learning is a subset of AI.",
                {"title": "ML Article", "source": "https://example.com/ml"},
            ),
            MockDocument(
                "Deep learning uses neural networks.",
                {"title": "DL Article", "source": "https://example.com/dl"},
            ),
        ]

        results = engine.run("artificial intelligence")

        assert len(results) == 2
        assert results[0]["title"] == "ML Article"
        assert results[0]["url"] == "https://example.com/ml"
        assert "Machine learning" in results[0]["snippet"]
        assert results[1]["title"] == "DL Article"

    def test_run_empty_results(self, engine):
        """Test search with no results."""
        engine.retriever.invoke.return_value = []

        results = engine.run("nonexistent topic")

        assert results == []

    def test_run_respects_max_results(self, engine):
        """Test that run respects max_results limit."""
        # Create 20 documents but engine has max_results=10
        docs = [
            MockDocument(f"Document {i} content", {"title": f"Doc {i}"})
            for i in range(20)
        ]
        engine.retriever.invoke.return_value = docs

        results = engine.run("test query")

        assert len(results) == 10

    def test_run_handles_exception(self, engine):
        """Test that exceptions are handled gracefully."""
        engine.retriever.invoke.side_effect = Exception("Retriever error")

        results = engine.run("test query")

        assert results == []

    def test_run_with_research_context(self, engine):
        """Test that run accepts research_context parameter."""
        engine.retriever.invoke.return_value = [
            MockDocument("Test content", {"title": "Test"})
        ]

        # Should not raise an error with research_context
        results = engine.run("test", research_context={"previous": "data"})

        assert len(results) == 1


class TestRetrieverDocumentConversion:
    """Tests for document conversion to LDR format."""

    @pytest.fixture
    def engine(self):
        """Create a Retriever engine."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "TestRetriever"
        return RetrieverSearchEngine(
            retriever=mock_retriever, name="TestRetriever"
        )

    def test_convert_document_with_full_metadata(self, engine):
        """Test document conversion with full metadata."""
        doc = MockDocument(
            "Full content here with lots of text.",
            {
                "title": "Test Document",
                "source": "https://example.com/doc",
                "author": "John Doe",
                "date": "2024-01-15",
                "score": 0.95,
            },
        )

        result = engine._convert_document_to_result(doc, 0)

        assert result["title"] == "Test Document"
        assert result["url"] == "https://example.com/doc"
        assert result["author"] == "John Doe"
        assert result["date"] == "2024-01-15"
        assert result["score"] == 0.95
        assert result["full_content"] == "Full content here with lots of text."
        assert result["source"] == "TestRetriever"

    def test_convert_document_with_minimal_metadata(self, engine):
        """Test document conversion with minimal metadata."""
        doc = MockDocument("Just some content", {})

        result = engine._convert_document_to_result(doc, 5)

        assert result["title"] == "Document 6"  # index 5 + 1
        assert "retriever://TestRetriever/doc_5" in result["url"]
        assert result["snippet"] == "Just some content"
        assert result["author"] == ""
        assert result["date"] == ""
        assert result["score"] == 1.0

    def test_convert_document_uses_url_if_no_source(self, engine):
        """Test that 'url' metadata is used if 'source' is not present."""
        doc = MockDocument("Content", {"url": "https://fallback.com/page"})

        result = engine._convert_document_to_result(doc, 0)

        assert result["url"] == "https://fallback.com/page"

    def test_convert_document_truncates_snippet(self, engine):
        """Test that long content is truncated for snippet."""
        long_content = "x" * 1000

        doc = MockDocument(long_content, {"title": "Long Doc"})

        result = engine._convert_document_to_result(doc, 0)

        assert len(result["snippet"]) == 500
        assert len(result["full_content"]) == 1000

    def test_convert_document_handles_none_content(self, engine):
        """Test that None content is handled."""
        doc = MockDocument(None, {"title": "Empty Doc"})
        doc.page_content = None

        result = engine._convert_document_to_result(doc, 0)

        assert result["snippet"] == ""
        assert result["full_content"] is None

    def test_convert_document_includes_metadata_dict(self, engine):
        """Test that full metadata dict is included."""
        doc = MockDocument(
            "Content",
            {"title": "Test", "custom_field": "custom_value", "another": 123},
        )

        result = engine._convert_document_to_result(doc, 0)

        assert result["metadata"]["custom_field"] == "custom_value"
        assert result["metadata"]["another"] == 123


class TestRetrieverPreviews:
    """Tests for Retriever preview methods."""

    @pytest.fixture
    def engine(self):
        """Create a Retriever engine."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "MockRetriever"
        return RetrieverSearchEngine(retriever=mock_retriever, max_results=5)

    def test_get_previews_success(self, engine):
        """Test successful preview retrieval."""
        engine.retriever.invoke.return_value = [
            MockDocument("Content 1", {"title": "Doc 1"}),
            MockDocument("Content 2", {"title": "Doc 2"}),
        ]

        previews = engine._get_previews("test query")

        assert len(previews) == 2
        assert previews[0]["title"] == "Doc 1"
        assert previews[1]["title"] == "Doc 2"

    def test_get_previews_respects_max_results(self, engine):
        """Test that previews respect max_results."""
        docs = [
            MockDocument(f"Content {i}", {"title": f"Doc {i}"})
            for i in range(10)
        ]
        engine.retriever.invoke.return_value = docs

        previews = engine._get_previews("test query")

        assert len(previews) == 5  # max_results is 5

    def test_get_previews_handles_exception(self, engine):
        """Test that preview exceptions are handled."""
        engine.retriever.invoke.side_effect = Exception("Error")

        previews = engine._get_previews("test query")

        assert previews == []


class TestRetrieverFullContent:
    """Tests for Retriever full content retrieval."""

    @pytest.fixture
    def engine(self):
        """Create a Retriever engine."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "MockRetriever"
        return RetrieverSearchEngine(retriever=mock_retriever)

    def test_get_full_content_returns_items(self, engine):
        """Test that full content returns items unchanged when full_content exists."""
        items = [
            {
                "title": "Doc 1",
                "snippet": "Snippet 1",
                "full_content": "Full 1",
            },
            {
                "title": "Doc 2",
                "snippet": "Snippet 2",
                "full_content": "Full 2",
            },
        ]

        results = engine._get_full_content(items)

        assert len(results) == 2
        assert results[0]["full_content"] == "Full 1"
        assert results[1]["full_content"] == "Full 2"

    def test_get_full_content_adds_missing_full_content(self, engine):
        """Test that missing full_content is populated from snippet."""
        items = [
            {"title": "Doc 1", "snippet": "Snippet as content"},
        ]

        results = engine._get_full_content(items)

        assert results[0]["full_content"] == "Snippet as content"


class TestRetrieverAsync:
    """Tests for Retriever async methods."""

    @pytest.fixture
    def engine(self):
        """Create a Retriever engine."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "MockRetriever"
        return RetrieverSearchEngine(retriever=mock_retriever)

    @pytest.mark.asyncio
    async def test_arun_with_async_retriever(self, engine):
        """Test async run with async-capable retriever."""
        async_mock = AsyncMock(
            return_value=[
                MockDocument("Async content", {"title": "Async Doc"}),
            ]
        )
        engine.retriever.aget_relevant_documents = async_mock

        results = await engine.arun("test query")

        assert len(results) == 1
        assert results[0]["title"] == "Async Doc"

    @pytest.mark.asyncio
    async def test_arun_falls_back_to_sync(self, engine):
        """Test async run falls back to sync when async not available."""
        # Remove async method if present
        if hasattr(engine.retriever, "aget_relevant_documents"):
            delattr(engine.retriever, "aget_relevant_documents")

        engine.retriever.invoke.return_value = [
            MockDocument("Sync content", {"title": "Sync Doc"})
        ]

        results = await engine.arun("test query")

        assert len(results) == 1
        assert results[0]["title"] == "Sync Doc"

    @pytest.mark.asyncio
    async def test_arun_handles_exception(self, engine):
        """Test that async exceptions are handled."""
        async_mock = AsyncMock(side_effect=Exception("Async error"))
        engine.retriever.aget_relevant_documents = async_mock

        results = await engine.arun("test query")

        assert results == []


class TestRetrieverResultFormat:
    """Tests for Retriever result format consistency."""

    @pytest.fixture
    def engine(self):
        """Create a Retriever engine."""
        from local_deep_research.web_search_engines.engines.search_engine_retriever import (
            RetrieverSearchEngine,
        )

        mock_retriever = Mock()
        mock_retriever.__class__.__name__ = "VectorStoreRetriever"
        return RetrieverSearchEngine(
            retriever=mock_retriever, name="MyVectorStore"
        )

    def test_result_has_required_fields(self, engine):
        """Test that results have all required LDR fields."""
        engine.retriever.invoke.return_value = [
            MockDocument("Test content", {"title": "Test"})
        ]

        results = engine.run("test")

        required_fields = ["title", "url", "snippet", "full_content", "source"]
        for field in required_fields:
            assert field in results[0], f"Missing required field: {field}"

    def test_result_includes_retriever_type(self, engine):
        """Test that results include retriever type."""
        engine.retriever.invoke.return_value = [
            MockDocument("Content", {"title": "Test"})
        ]

        results = engine.run("test")

        assert results[0]["retriever_type"] == "VectorStoreRetriever"
        assert results[0]["source"] == "MyVectorStore"
