"""
Comprehensive tests for the Tavily search engine.
Tests initialization, search functionality, error handling, and domain filtering.

Note: These tests mock HTTP requests to avoid requiring an API key.
"""

import pytest
from unittest.mock import Mock


class TestTavilySearchEngineInit:
    """Tests for Tavily search engine initialization."""

    def test_init_with_api_key(self, monkeypatch):
        """Test initialization with API key."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_api_key")

        assert engine.api_key == "test_api_key"
        assert engine.max_results >= 10
        assert engine.is_public is True
        assert engine.is_generic is True

    def test_init_custom_max_results(self, monkeypatch):
        """Test initialization with custom max results."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key", max_results=25)
        assert engine.max_results >= 25

    def test_init_without_api_key_raises(self, monkeypatch):
        """Test that initialization without API key raises ValueError."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        with pytest.raises(ValueError, match="Tavily API key not found"):
            TavilySearchEngine()

    def test_init_with_search_depth(self, monkeypatch):
        """Test initialization with search depth options."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine_basic = TavilySearchEngine(
            api_key="test_key", search_depth="basic"
        )
        assert engine_basic.search_depth == "basic"

        engine_advanced = TavilySearchEngine(
            api_key="test_key", search_depth="advanced"
        )
        assert engine_advanced.search_depth == "advanced"

    def test_base_url_configured(self, monkeypatch):
        """Test that base URL is properly configured."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key")
        assert engine.base_url == "https://api.tavily.com"


class TestTavilyDomainFiltering:
    """Tests for Tavily domain filtering."""

    @pytest.fixture(autouse=True)
    def mock_get_setting(self, monkeypatch):
        """Mock get_setting_from_snapshot."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

    def test_include_domains_default(self):
        """Test that include_domains defaults to empty list."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key")
        assert engine.include_domains == []

    def test_include_domains_custom(self):
        """Test custom include_domains."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(
            api_key="test_key", include_domains=["example.com", "test.com"]
        )
        assert engine.include_domains == ["example.com", "test.com"]

    def test_exclude_domains_default(self):
        """Test that exclude_domains defaults to empty list."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key")
        assert engine.exclude_domains == []

    def test_exclude_domains_custom(self):
        """Test custom exclude_domains."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(
            api_key="test_key", exclude_domains=["spam.com", "ads.com"]
        )
        assert engine.exclude_domains == ["spam.com", "ads.com"]


class TestTavilyEngineType:
    """Tests for Tavily engine type identification."""

    @pytest.fixture(autouse=True)
    def mock_get_setting(self, monkeypatch):
        """Mock get_setting_from_snapshot."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

    def test_engine_type_set(self):
        """Test that engine type is properly set."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key")
        assert "tavily" in engine.engine_type.lower()

    def test_engine_is_public(self):
        """Test that engine is marked as public."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key")
        assert engine.is_public is True

    def test_engine_is_generic(self):
        """Test that engine is marked as generic."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key")
        assert engine.is_generic is True


class TestTavilySearchExecution:
    """Tests for Tavily search execution."""

    @pytest.fixture
    def engine(self, monkeypatch):
        """Create a Tavily engine with mocked settings."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        return TavilySearchEngine(
            api_key="test_key", include_full_content=False
        )

    def test_get_previews_success(self, engine, monkeypatch):
        """Test successful preview retrieval."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "results": [
                    {
                        "title": "Test Result 1",
                        "url": "https://example1.com",
                        "content": "This is test content 1",
                    },
                    {
                        "title": "Test Result 2",
                        "url": "https://example2.com",
                        "content": "This is test content 2",
                    },
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test query")

        assert len(previews) == 2
        assert previews[0]["title"] == "Test Result 1"
        assert previews[1]["link"] == "https://example2.com"
        assert previews[0]["snippet"] == "This is test content 1"

    def test_get_previews_empty_results(self, engine, monkeypatch):
        """Test preview retrieval with no results."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"results": []})

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test query")

        assert previews == []

    def test_get_previews_handles_exception(self, engine, monkeypatch):
        """Test that exceptions are handled gracefully."""
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            Mock(side_effect=Exception("Network error")),
        )

        previews = engine._get_previews("test query")

        assert previews == []


class TestTavilyRateLimiting:
    """Tests for Tavily rate limit handling."""

    @pytest.fixture
    def engine(self, monkeypatch):
        """Create a Tavily engine with mocked settings."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        return TavilySearchEngine(
            api_key="test_key", include_full_content=False
        )

    def test_rate_limit_429_raises_error(self, engine, monkeypatch):
        """Test that 429 errors raise RateLimitError."""
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        mock_response = Mock()
        mock_response.status_code = 429
        mock_response.text = "Too Many Requests"

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            Mock(return_value=mock_response),
        )

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

    def test_rate_limit_in_exception_raises_error(self, engine, monkeypatch):
        """Test that rate limit patterns in exceptions raise RateLimitError."""
        import requests

        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            Mock(
                side_effect=requests.exceptions.RequestException(
                    "rate limit exceeded"
                )
            ),
        )

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


class TestTavilyFullContent:
    """Tests for Tavily full content retrieval."""

    @pytest.fixture(autouse=True)
    def mock_get_setting(self, monkeypatch):
        """Mock get_setting_from_snapshot."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

    def test_include_full_content_flag_true(self):
        """Test include_full_content flag is set correctly when True."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(
            api_key="test_key", include_full_content=True
        )
        assert engine.include_full_content is True

    def test_include_full_content_flag_false(self):
        """Test include_full_content flag is set correctly when False."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(
            api_key="test_key", include_full_content=False
        )
        assert engine.include_full_content is False

    def test_get_full_content_returns_results(self):
        """Test that full content returns processed results."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(
            api_key="test_key", include_full_content=False
        )

        items = [
            {
                "title": "Test",
                "link": "https://example.com",
                "_full_result": {
                    "title": "Full Test",
                    "content": "Full content",
                },
            },
        ]

        results = engine._get_full_content(items)

        assert len(results) == 1


class TestTavilySearchDepth:
    """Tests for Tavily search depth configuration."""

    @pytest.fixture(autouse=True)
    def mock_get_setting(self, monkeypatch):
        """Mock get_setting_from_snapshot."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

    def test_search_depth_default(self):
        """Test that search_depth defaults to basic."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key")
        assert engine.search_depth == "basic"

    def test_search_depth_basic(self):
        """Test search_depth set to basic."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key", search_depth="basic")
        assert engine.search_depth == "basic"

    def test_search_depth_advanced(self):
        """Test search_depth set to advanced."""
        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        engine = TavilySearchEngine(api_key="test_key", search_depth="advanced")
        assert engine.search_depth == "advanced"


class TestTavilyQueryHandling:
    """Tests for Tavily query handling."""

    @pytest.fixture
    def engine(self, monkeypatch):
        """Create a Tavily engine with mocked settings."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        return TavilySearchEngine(
            api_key="test_key", include_full_content=False
        )

    def test_query_truncation(self, engine, monkeypatch):
        """Test that long queries are truncated to 400 chars."""
        captured_payload = {}

        def mock_post(url, json=None, **kwargs):
            captured_payload.update(json)
            mock_response = Mock()
            mock_response.status_code = 200
            mock_response.raise_for_status = Mock()
            mock_response.json = Mock(return_value={"results": []})
            return mock_response

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            mock_post,
        )

        long_query = "a" * 500
        engine._get_previews(long_query)

        # Query should be truncated
        assert len(captured_payload.get("query", "")) == 400


class TestTavilyRun:
    """Tests for Tavily run method."""

    @pytest.fixture
    def engine(self, monkeypatch):
        """Create a Tavily engine with mocked settings."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        return TavilySearchEngine(
            api_key="test_key", include_full_content=False
        )

    def test_run_cleans_up_search_results(self, engine, monkeypatch):
        """Test that run method cleans up _search_results attribute."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "results": [
                    {
                        "title": "Test",
                        "url": "https://example.com",
                        "content": "Content",
                    },
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            Mock(return_value=mock_response),
        )

        # Run should clean up _search_results
        engine.run("test query")

        assert not hasattr(engine, "_search_results")

    def test_run_returns_results(self, engine, monkeypatch):
        """Test that run method returns results."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "results": [
                    {
                        "title": "Test Result",
                        "url": "https://example.com",
                        "content": "Test content",
                    },
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            Mock(return_value=mock_response),
        )

        results = engine.run("test query")

        assert isinstance(results, list)


class TestTavilyAPIPayload:
    """Tests for Tavily API payload construction."""

    @pytest.fixture
    def engine(self, monkeypatch):
        """Create a Tavily engine with mocked settings."""
        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        from local_deep_research.web_search_engines.engines.search_engine_tavily import (
            TavilySearchEngine,
        )

        return TavilySearchEngine(
            api_key="test_key",
            include_full_content=False,
            include_domains=["example.com"],
            exclude_domains=["spam.com"],
        )

    def test_payload_includes_domain_filters(self, engine, monkeypatch):
        """Test that payload includes domain filters when set."""
        captured_payload = {}

        def mock_post(url, json=None, **kwargs):
            captured_payload.update(json)
            mock_response = Mock()
            mock_response.status_code = 200
            mock_response.raise_for_status = Mock()
            mock_response.json = Mock(return_value={"results": []})
            return mock_response

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            mock_post,
        )

        engine._get_previews("test query")

        assert captured_payload.get("include_domains") == ["example.com"]
        assert captured_payload.get("exclude_domains") == ["spam.com"]

    def test_payload_includes_search_depth(self, engine, monkeypatch):
        """Test that payload includes search depth."""
        captured_payload = {}

        def mock_post(url, json=None, **kwargs):
            captured_payload.update(json)
            mock_response = Mock()
            mock_response.status_code = 200
            mock_response.raise_for_status = Mock()
            mock_response.json = Mock(return_value={"results": []})
            return mock_response

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_tavily.safe_post",
            mock_post,
        )

        engine._get_previews("test query")

        assert captured_payload.get("search_depth") == "basic"
