"""
Tests for the ArXivSearchEngine class.

Tests cover:
- Initialization and configuration
- Sort criteria and order
- Preview generation
- Full content retrieval with PDF handling
- Rate limit error handling
- Run method
- Helper methods (get_paper_details, search_by_author, search_by_category)
"""

from datetime import datetime
from unittest.mock import Mock, patch
import pytest


class TestArXivSearchEngineInit:
    """Tests for ArXivSearchEngine initialization."""

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

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            assert engine.max_results >= 25  # Minimum 25
            assert engine.sort_by == "relevance"
            assert engine.sort_order == "descending"
            assert engine.include_full_text is False
            assert engine.download_dir is None
            assert engine.max_full_text == 1

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

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine(max_results=50)

            assert engine.max_results == 50

    def test_init_max_results_minimum_enforced(self):
        """Initialize with low max_results gets bumped to 25."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine(max_results=5)

            assert engine.max_results >= 25

    def test_init_with_custom_sort_by(self):
        """Initialize with custom sort_by."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine(sort_by="submittedDate")

            assert engine.sort_by == "submittedDate"

    def test_init_with_custom_sort_order(self):
        """Initialize with custom sort_order."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine(sort_order="ascending")

            assert engine.sort_order == "ascending"

    def test_init_with_full_text_enabled(self):
        """Initialize with include_full_text enabled."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine(
                include_full_text=True, download_dir="/tmp/papers"
            )

            assert engine.include_full_text is True
            assert engine.download_dir == "/tmp/papers"

    def test_init_with_max_full_text(self):
        """Initialize with custom max_full_text."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine(max_full_text=5)

            assert engine.max_full_text == 5

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

        mock_llm = Mock()
        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine(llm=mock_llm)

            assert engine.llm is mock_llm

    def test_init_with_journal_filter(self):
        """Initialize with journal reputation filter."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        mock_filter = Mock()
        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=mock_filter,
        ):
            engine = ArXivSearchEngine()

            assert mock_filter in engine._preview_filters

    def test_init_sort_criteria_mapping(self):
        """Initialize sets up sort criteria mapping."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )
        import arxiv

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            assert (
                engine.sort_criteria["relevance"]
                == arxiv.SortCriterion.Relevance
            )
            assert (
                engine.sort_criteria["lastUpdatedDate"]
                == arxiv.SortCriterion.LastUpdatedDate
            )
            assert (
                engine.sort_criteria["submittedDate"]
                == arxiv.SortCriterion.SubmittedDate
            )


class TestGetSearchResults:
    """Tests for _get_search_results method."""

    def test_get_search_results_creates_client(self):
        """Get search results creates arxiv client."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            with patch("arxiv.Client") as mock_client_class:
                with patch("arxiv.Search"):
                    mock_client = Mock()
                    mock_client.results.return_value = iter([])
                    mock_client_class.return_value = mock_client

                    engine = ArXivSearchEngine()
                    engine._get_search_results("test query")

                    mock_client_class.assert_called_once()


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

    def test_get_previews_returns_results(self):
        """Get previews returns formatted results."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        mock_paper = Mock()
        mock_paper.entry_id = "https://arxiv.org/abs/2101.12345"
        mock_paper.title = "Test Paper"
        mock_paper.summary = "This is a test paper summary"
        mock_paper.authors = [Mock(name="Author 1"), Mock(name="Author 2")]
        mock_paper.published = datetime(2021, 1, 15)
        mock_paper.journal_ref = "Journal Ref"

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine, "_get_search_results", return_value=[mock_paper]
            ):
                previews = engine._get_previews("test query")

                assert len(previews) == 1
                assert previews[0]["id"] == "https://arxiv.org/abs/2101.12345"
                assert previews[0]["title"] == "Test Paper"
                assert previews[0]["link"] == "https://arxiv.org/abs/2101.12345"
                assert previews[0]["source"] == "arXiv"

    def test_get_previews_truncates_long_summary(self):
        """Get previews truncates long summaries."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        mock_paper = Mock()
        mock_paper.entry_id = "https://arxiv.org/abs/2101.12345"
        mock_paper.title = "Test Paper"
        mock_paper.summary = "x" * 300  # Long summary
        mock_paper.authors = []
        mock_paper.published = datetime(2021, 1, 15)
        mock_paper.journal_ref = None

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine, "_get_search_results", return_value=[mock_paper]
            ):
                previews = engine._get_previews("test query")

                assert len(previews[0]["snippet"]) == 253  # 250 + "..."

    def test_get_previews_handles_no_publish_date(self):
        """Get previews handles papers without publish date."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        mock_paper = Mock()
        mock_paper.entry_id = "https://arxiv.org/abs/2101.12345"
        mock_paper.title = "Test Paper"
        mock_paper.summary = "Summary"
        mock_paper.authors = []
        mock_paper.published = None
        mock_paper.journal_ref = None

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine, "_get_search_results", return_value=[mock_paper]
            ):
                previews = engine._get_previews("test query")

                assert previews[0]["published"] is None

    def test_get_previews_empty_results(self):
        """Get previews handles empty results."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(engine, "_get_search_results", return_value=[]):
                previews = engine._get_previews("test query")

                assert previews == []

    def test_get_previews_rate_limit_429_error(self):
        """Get previews raises RateLimitError on 429."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine,
                "_get_search_results",
                side_effect=Exception("Error 429: Too many requests"),
            ):
                with pytest.raises(RateLimitError):
                    engine._get_previews("test query")

    def test_get_previews_rate_limit_503_error(self):
        """Get previews raises RateLimitError on 503 service unavailable."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine,
                "_get_search_results",
                side_effect=Exception("503 Service Unavailable"),
            ):
                with pytest.raises(RateLimitError):
                    engine._get_previews("test query")

    def test_get_previews_general_error_returns_empty(self):
        """Get previews returns empty list on general error."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine,
                "_get_search_results",
                side_effect=Exception("Connection error"),
            ):
                previews = engine._get_previews("test query")

                assert previews == []

    def test_get_previews_stores_papers_cache(self):
        """Get previews stores papers in cache."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        mock_paper = Mock()
        mock_paper.entry_id = "https://arxiv.org/abs/2101.12345"
        mock_paper.title = "Test Paper"
        mock_paper.summary = "Summary"
        mock_paper.authors = []
        mock_paper.published = datetime(2021, 1, 15)
        mock_paper.journal_ref = None

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine, "_get_search_results", return_value=[mock_paper]
            ):
                engine._get_previews("test query")

                assert hasattr(engine, "_papers")
                assert "https://arxiv.org/abs/2101.12345" in engine._papers


class TestGetFullContent:
    """Tests for _get_full_content method."""

    def test_get_full_content_returns_items(self):
        """Get full content returns items with full paper info."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        mock_paper = Mock()
        mock_paper.pdf_url = "https://arxiv.org/pdf/2101.12345.pdf"
        mock_paper.authors = [Mock(name="Author 1")]
        mock_paper.published = datetime(2021, 1, 15)
        mock_paper.updated = datetime(2021, 2, 15)
        mock_paper.categories = ["cs.AI"]
        mock_paper.summary = "Full summary"
        mock_paper.comment = "Comment"
        mock_paper.doi = "10.1234/test"

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()
            engine._papers = {"paper-id": mock_paper}

            items = [{"id": "paper-id", "title": "Test Paper"}]
            results = engine._get_full_content(items)

            assert len(results) == 1
            assert (
                results[0]["pdf_url"] == "https://arxiv.org/pdf/2101.12345.pdf"
            )
            assert results[0]["summary"] == "Full summary"
            assert results[0]["content"] == "Full summary"

    def test_get_full_content_without_cached_paper(self):
        """Get full content handles items not in cache."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()
            engine._papers = {}

            items = [{"id": "unknown-id", "title": "Test Paper"}]
            results = engine._get_full_content(items)

            assert len(results) == 1
            assert results[0]["title"] == "Test Paper"

    def test_get_full_content_snippets_only_mode(self):
        """Get full content in snippets only mode."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )
        from local_deep_research.web_search_engines.engines import (
            search_engine_arxiv,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                search_engine_arxiv.search_config,
                "SEARCH_SNIPPETS_ONLY",
                True,
                create=True,
            ):
                items = [{"id": "paper-id", "title": "Test Paper"}]
                results = engine._get_full_content(items)

                assert results == items


class TestRun:
    """Tests for run method."""

    def test_run_returns_results(self):
        """Run returns search results."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(
                engine,
                "_get_previews",
                return_value=[
                    {
                        "id": "https://arxiv.org/abs/2101.12345",
                        "title": "Result",
                        "snippet": "Snippet",
                        "link": "https://arxiv.org/abs/2101.12345",
                    }
                ],
            ):
                with patch.object(
                    engine,
                    "_get_full_content",
                    return_value=[{"title": "Result", "content": "Full"}],
                ):
                    results = engine.run("test query")

                    assert len(results) == 1

    def test_run_handles_empty_results(self):
        """Run handles empty results."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(engine, "_get_previews", return_value=[]):
                results = engine.run("test query")

                assert results == []

    def test_run_cleans_up_papers_cache(self):
        """Run cleans up _papers after execution."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()
            engine._papers = {"test": "data"}

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

                assert not hasattr(engine, "_papers")


class TestGetPaperDetails:
    """Tests for get_paper_details method."""

    def test_get_paper_details_returns_info(self):
        """Get paper details returns paper information."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        mock_paper = Mock()
        mock_paper.entry_id = "https://arxiv.org/abs/2101.12345"
        mock_paper.title = "Test Paper"
        mock_paper.summary = "Short summary"
        mock_paper.authors = [Mock(name="Author 1")]
        mock_paper.published = datetime(2021, 1, 15)
        mock_paper.updated = datetime(2021, 2, 15)
        mock_paper.journal_ref = "Journal Ref"
        mock_paper.pdf_url = "https://arxiv.org/pdf/2101.12345.pdf"
        mock_paper.categories = ["cs.AI"]
        mock_paper.comment = "Comment"
        mock_paper.doi = "10.1234/test"

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            with patch("arxiv.Client") as mock_client_class:
                with patch("arxiv.Search"):
                    mock_client = Mock()
                    mock_client.results.return_value = iter([mock_paper])
                    mock_client_class.return_value = mock_client

                    engine = ArXivSearchEngine()
                    result = engine.get_paper_details("2101.12345")

                    assert result["title"] == "Test Paper"
                    assert result["link"] == "https://arxiv.org/abs/2101.12345"

    def test_get_paper_details_not_found(self):
        """Get paper details returns empty for non-existent paper."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            with patch("arxiv.Client") as mock_client_class:
                with patch("arxiv.Search"):
                    mock_client = Mock()
                    mock_client.results.return_value = iter([])
                    mock_client_class.return_value = mock_client

                    engine = ArXivSearchEngine()
                    result = engine.get_paper_details("invalid-id")

                    assert result == {}

    def test_get_paper_details_handles_error(self):
        """Get paper details handles errors gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            with patch("arxiv.Client", side_effect=Exception("API error")):
                engine = ArXivSearchEngine()
                result = engine.get_paper_details("2101.12345")

                assert result == {}


class TestSearchByAuthor:
    """Tests for search_by_author method."""

    def test_search_by_author_formats_query(self):
        """Search by author formats query correctly."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(engine, "run", return_value=[]) as mock_run:
                engine.search_by_author("John Doe")

                mock_run.assert_called_once_with('au:"John Doe"')

    def test_search_by_author_with_max_results(self):
        """Search by author respects max_results parameter."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()
            original_max = engine.max_results

            with patch.object(engine, "run", return_value=[]):
                engine.search_by_author("John Doe", max_results=50)

                # Should restore original value after search
                assert engine.max_results == original_max


class TestSearchByCategory:
    """Tests for search_by_category method."""

    def test_search_by_category_formats_query(self):
        """Search by category formats query correctly."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()

            with patch.object(engine, "run", return_value=[]) as mock_run:
                engine.search_by_category("cs.AI")

                mock_run.assert_called_once_with("cat:cs.AI")

    def test_search_by_category_with_max_results(self):
        """Search by category respects max_results parameter."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        with patch(
            "local_deep_research.advanced_search_system.filters.journal_reputation_filter.JournalReputationFilter.create_default",
            return_value=None,
        ):
            engine = ArXivSearchEngine()
            original_max = engine.max_results

            with patch.object(engine, "run", return_value=[]):
                engine.search_by_category("cs.AI", max_results=50)

                # Should restore original value after search
                assert engine.max_results == original_max


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

    def test_is_public(self):
        """ArXivSearchEngine is marked as public."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        assert ArXivSearchEngine.is_public is True

    def test_is_generic_false(self):
        """ArXivSearchEngine is not marked as generic."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        assert ArXivSearchEngine.is_generic is False

    def test_is_scientific(self):
        """ArXivSearchEngine is marked as scientific."""
        from local_deep_research.web_search_engines.engines.search_engine_arxiv import (
            ArXivSearchEngine,
        )

        assert ArXivSearchEngine.is_scientific is True
