"""
Tests for the GuardianSearchEngine class.

Tests cover:
- Initialization and configuration
- API key handling
- Date range handling
- Query optimization
- Adaptive search strategies
- Preview generation
- Full content retrieval
- Helper methods
"""

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


class TestGuardianSearchEngineInit:
    """Tests for GuardianSearchEngine initialization."""

    def test_init_with_api_key(self):
        """Initialize with API key."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-api-key")

        assert engine.api_key == "test-api-key"
        assert engine.max_results == 10
        assert engine.order_by == "relevance"
        assert engine.optimize_queries is True
        assert engine.adaptive_search is True

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

        engine = GuardianSearchEngine(api_key="test-key", max_results=25)

        assert engine.max_results == 25

    def test_init_with_custom_dates(self):
        """Initialize with custom date range."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(
            api_key="test-key", from_date="2024-01-01", to_date="2024-06-01"
        )

        assert engine.from_date == "2024-01-01"
        assert engine.to_date == "2024-06-01"

    def test_init_with_default_dates(self):
        """Initialize with default date range (last month)."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        # Check that from_date is approximately 30 days ago
        expected_from = (datetime.now(UTC) - timedelta(days=30)).strftime(
            "%Y-%m-%d"
        )
        assert engine.from_date == expected_from

    def test_init_with_section(self):
        """Initialize with section filter."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key", section="technology")

        assert engine.section == "technology"

    def test_init_with_order_by(self):
        """Initialize with custom order_by."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key", order_by="newest")

        assert engine.order_by == "newest"

    def test_init_without_api_key_raises(self):
        """Initialize without API key raises ValueError."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        with pytest.raises(
            ValueError, match="No valid API key found for Guardian"
        ):
            GuardianSearchEngine()

    def test_init_with_api_key_from_settings(self):
        """Initialize with API key from settings."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(
            settings_snapshot={
                "search.engine.web.guardian.api_key": "settings-api-key"
            }
        )

        assert engine.api_key == "settings-api-key"

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

        mock_llm = Mock()
        engine = GuardianSearchEngine(api_key="test-key", llm=mock_llm)

        assert engine.llm is mock_llm

    def test_init_stores_original_date_params(self):
        """Initialize stores original date parameters."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(
            api_key="test-key", from_date="2024-01-01", to_date="2024-06-01"
        )

        assert engine._original_date_params["from_date"] == "2024-01-01"
        assert engine._original_date_params["to_date"] == "2024-06-01"


class TestOptimizeQueryForGuardian:
    """Tests for _optimize_query_for_guardian method."""

    def test_optimize_query_without_llm(self):
        """Optimize query returns original without LLM."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        result = engine._optimize_query_for_guardian("What is the latest news?")

        assert result == "What is the latest news?"

    def test_optimize_query_with_optimization_disabled(self):
        """Optimize query returns original when disabled."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        mock_llm = Mock()
        engine = GuardianSearchEngine(
            api_key="test-key", llm=mock_llm, optimize_queries=False
        )

        result = engine._optimize_query_for_guardian("What is the latest news?")

        assert result == "What is the latest news?"
        mock_llm.invoke.assert_not_called()

    def test_optimize_query_truncates_long_queries(self):
        """Optimize query truncates queries longer than 150 chars."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")
        long_query = " ".join(["word"] * 50)  # Very long query

        result = engine._optimize_query_for_guardian(long_query)

        # Should be truncated to first 10 words
        assert len(result.split()) <= 10

    def test_optimize_query_with_llm(self):
        """Optimize query uses LLM when available."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        mock_llm = Mock()
        mock_llm.invoke.return_value = Mock(content="UK politics")

        engine = GuardianSearchEngine(api_key="test-key", llm=mock_llm)

        result = engine._optimize_query_for_guardian(
            "What is happening in UK politics?"
        )

        assert result == "UK politics"


class TestAdaptDatesForQueryType:
    """Tests for _adapt_dates_for_query_type method."""

    def test_adapt_dates_short_query(self):
        """Adapt dates defaults to recent news for short queries."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        engine._adapt_dates_for_query_type("test")

        # Short query should use newest ordering
        assert engine.order_by == "newest"

    def test_adapt_dates_without_llm(self):
        """Adapt dates does nothing without LLM for longer queries."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        engine._adapt_dates_for_query_type("this is a longer query for testing")

        # Without LLM, dates shouldn't change significantly for non-short queries
        # (but it still might classify it as short)


class TestGetAllData:
    """Tests for _get_all_data method."""

    def test_get_all_data_returns_articles(self):
        """Get all data returns formatted articles."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        mock_response = Mock()
        mock_response.json.return_value = {
            "response": {
                "results": [
                    {
                        "id": "article-1",
                        "webTitle": "Test Article",
                        "webUrl": "https://guardian.com/article-1",
                        "webPublicationDate": "2024-01-15",
                        "sectionName": "Technology",
                        "fields": {
                            "headline": "Test Article",
                            "trailText": "This is a test",
                            "byline": "John Doe",
                            "body": "Full content here",
                        },
                        "tags": [],
                    }
                ]
            }
        }
        mock_response.raise_for_status = Mock()

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_guardian.safe_get",
            return_value=mock_response,
        ):
            engine = GuardianSearchEngine(api_key="test-key")
            articles = engine._get_all_data("test query")

            assert len(articles) == 1
            assert articles[0]["title"] == "Test Article"
            assert articles[0]["link"] == "https://guardian.com/article-1"
            assert articles[0]["content"] == "Full content here"

    def test_get_all_data_empty_query(self):
        """Get all data handles empty query."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        mock_response = Mock()
        mock_response.json.return_value = {"response": {"results": []}}
        mock_response.raise_for_status = Mock()

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_guardian.safe_get",
            return_value=mock_response,
        ) as mock_get:
            engine = GuardianSearchEngine(api_key="test-key")
            engine._get_all_data("")

            # Should use 'news' as default query
            call_kwargs = mock_get.call_args[1]
            assert call_kwargs["params"]["q"] == "news"

    def test_get_all_data_handles_error(self):
        """Get all data handles errors gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_guardian.safe_get",
            side_effect=Exception("API error"),
        ):
            engine = GuardianSearchEngine(api_key="test-key")
            articles = engine._get_all_data("test query")

            assert articles == []

    def test_get_all_data_rate_limit_error(self):
        """Get all data raises RateLimitError for rate limit exceptions."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_guardian.safe_get",
            side_effect=Exception("429 Too Many Requests: rate limit exceeded"),
        ):
            engine = GuardianSearchEngine(api_key="test-key")

            with pytest.raises(RateLimitError):
                engine._get_all_data("test query")

    def test_get_all_data_rate_limit_error_reraise(self):
        """Get all data re-raises RateLimitError directly."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_guardian.safe_get",
            side_effect=RateLimitError("Rate limit hit"),
        ):
            engine = GuardianSearchEngine(api_key="test-key")

            with pytest.raises(RateLimitError, match="Rate limit hit"):
                engine._get_all_data("test query")


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

    def test_get_previews_returns_results(self):
        """Get previews returns formatted preview data."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        mock_articles = [
            {
                "id": "article-1",
                "title": "Test Article",
                "link": "https://guardian.com/article-1",
                "snippet": "This is a test",
                "publication_date": "2024-01-15",
                "section": "Technology",
                "author": "John Doe",
                "content": "Full content",
                "full_content": "Full content",
                "keywords": ["tech"],
            }
        ]

        with patch.object(
            engine, "_optimize_query_for_guardian", return_value="test query"
        ):
            with patch.object(engine, "_adapt_dates_for_query_type"):
                with patch.object(
                    engine,
                    "_adaptive_search",
                    return_value=(mock_articles, "initial"),
                ):
                    previews = engine._get_previews("test query")

                    assert len(previews) == 1
                    assert previews[0]["title"] == "Test Article"
                    assert previews[0]["section"] == "Technology"


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

    def test_get_full_content_returns_cached_articles(self):
        """Get full content returns cached full articles."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")
        engine._full_articles = {
            "article-1": {
                "id": "article-1",
                "title": "Test Article",
                "content": "Full content here",
            }
        }

        items = [{"id": "article-1", "title": "Test Article"}]
        results = engine._get_full_content(items)

        assert len(results) == 1
        assert results[0]["content"] == "Full content here"

    def test_get_full_content_without_cache(self):
        """Get full content returns preview if not in cache."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")
        engine._full_articles = {}

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

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


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_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        mock_previews = [
            {
                "id": "article-1",
                "title": "Test Article",
                "link": "https://guardian.com/article-1",
                "snippet": "Test snippet",
                "publication_date": "2024-01-15",
                "section": "Technology",
                "author": "John Doe",
                "keywords": [],
            }
        ]

        with patch.object(engine, "_get_previews", return_value=mock_previews):
            with patch.object(
                engine,
                "_get_full_content",
                return_value=[{"title": "Test Article", "content": "Full"}],
            ):
                results = engine.run("test query")

                assert len(results) == 1
                assert results[0]["source"] == "The Guardian"

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

        engine = GuardianSearchEngine(api_key="test-key")

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

            assert results == []

    def test_run_handles_none_query(self):
        """Run handles None query."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        with patch.object(
            engine, "_get_previews", return_value=[]
        ) as mock_get_previews:
            engine.run(None)

            # Should use 'news' as default
            mock_get_previews.assert_called()


class TestSearchBySection:
    """Tests for search_by_section method."""

    def test_search_by_section(self):
        """Search by section sets section parameter."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")

        with patch.object(engine, "run", return_value=[]) as mock_run:
            engine.search_by_section("technology")

            mock_run.assert_called_once_with("")
            # Section should be set temporarily
            assert engine.section is None  # Restored after call


class TestGetRecentArticles:
    """Tests for get_recent_articles method."""

    def test_get_recent_articles(self):
        """Get recent articles sets appropriate parameters."""
        from local_deep_research.web_search_engines.engines.search_engine_guardian import (
            GuardianSearchEngine,
        )

        engine = GuardianSearchEngine(api_key="test-key")
        original_order = engine.order_by

        with patch.object(engine, "run", return_value=[]) as mock_run:
            engine.get_recent_articles(days=7)

            mock_run.assert_called_once_with("")
            # Order should be restored after call
            assert engine.order_by == original_order


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

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

        assert GuardianSearchEngine.is_public is True
