"""Tests for web search tool."""

import pytest
from promethian.tools.web_search import (
    WebSearchTool,
    WebSearchResult,
    SearchResult,
    get_web_search,
    reset_web_search,
)


@pytest.fixture
def web_search_tool():
    """Create a fresh web search tool for testing."""
    reset_web_search()
    return get_web_search()


@pytest.fixture(autouse=True)
def cleanup():
    """Reset web search tool after each test."""
    yield
    reset_web_search()


class TestSearchResult:
    """Tests for SearchResult dataclass."""

    def test_search_result(self):
        result = SearchResult(
            title="Test Title",
            url="https://example.com",
            snippet="This is a test snippet",
        )
        assert result.title == "Test Title"
        assert result.url == "https://example.com"
        assert result.snippet == "This is a test snippet"


class TestWebSearchResult:
    """Tests for WebSearchResult dataclass."""

    def test_success_result(self):
        result = WebSearchResult(
            success=True,
            results=[
                SearchResult(title="Test", url="https://example.com", snippet="Snippet"),
            ],
            query="test query",
        )
        assert result.success is True
        assert len(result.results) == 1
        assert result.query == "test query"

    def test_failure_result(self):
        result = WebSearchResult(
            success=False,
            error="Search failed",
            query="test query",
        )
        assert result.success is False
        assert result.error == "Search failed"
        assert result.results is None


class TestWebSearchTool:
    """Tests for WebSearchTool class."""

    @pytest.mark.asyncio
    async def test_search_returns_results(self, web_search_tool):
        """Test that search returns results."""
        result = await web_search_tool.search("python programming", max_results=3)
        assert result.success is True
        assert result.results is not None
        assert len(result.results) > 0
        assert result.query == "python programming"

        # Check result structure
        for r in result.results:
            assert isinstance(r.title, str)
            assert isinstance(r.url, str)
            assert isinstance(r.snippet, str)

    @pytest.mark.asyncio
    async def test_search_respects_max_results(self, web_search_tool):
        """Test that search respects max_results parameter."""
        result = await web_search_tool.search("programming", max_results=2)
        assert result.success is True
        assert result.results is not None
        assert len(result.results) <= 2

    @pytest.mark.asyncio
    async def test_news_search(self, web_search_tool):
        """Test news search returns results."""
        result = await web_search_tool.news_search("technology", max_results=3)
        assert result.success is True
        assert result.results is not None
        # News results might be empty if no recent news matches
        assert result.query == "technology"


class TestWebSearchToolSchemas:
    """Tests for tool schema generation."""

    def test_get_tool_schemas(self, web_search_tool):
        """Test that schemas are generated correctly."""
        schemas = web_search_tool.get_tool_schemas()
        assert len(schemas) == 2

        # Check web_search schema
        search_schema = next(s for s in schemas if s["name"] == "web_search")
        assert "query" in search_schema["input_schema"]["properties"]
        assert "query" in search_schema["input_schema"]["required"]
        assert "max_results" in search_schema["input_schema"]["properties"]

        # Check web_news_search schema
        news_schema = next(s for s in schemas if s["name"] == "web_news_search")
        assert "query" in news_schema["input_schema"]["properties"]
        assert "query" in news_schema["input_schema"]["required"]


class TestWebSearchToolExecute:
    """Tests for execute_tool method."""

    @pytest.mark.asyncio
    async def test_execute_web_search(self, web_search_tool):
        """Test executing via execute_tool method."""
        result = await web_search_tool.execute_tool(
            "web_search",
            {"query": "python tutorials", "max_results": 2},
        )
        assert result.success is True
        assert result.results is not None

    @pytest.mark.asyncio
    async def test_execute_news_search(self, web_search_tool):
        """Test executing news search via execute_tool method."""
        result = await web_search_tool.execute_tool(
            "web_news_search",
            {"query": "technology news", "max_results": 3},
        )
        # News search may occasionally fail due to rate limits or no results
        # Just verify the structure is correct
        assert result.query == "technology news"
        if result.success:
            assert result.results is not None or result.results == []

    @pytest.mark.asyncio
    async def test_execute_unknown_tool(self, web_search_tool):
        """Test executing unknown tool name."""
        result = await web_search_tool.execute_tool(
            "unknown_tool",
            {"query": "test"},
        )
        assert result.success is False
        assert "unknown" in result.error.lower()

    @pytest.mark.asyncio
    async def test_max_results_capped_at_10(self, web_search_tool):
        """Test that max_results is capped at 10."""
        result = await web_search_tool.execute_tool(
            "web_search",
            {"query": "python", "max_results": 100},  # Request 100
        )
        assert result.success is True
        # Should be capped at 10
        assert result.results is not None
        assert len(result.results) <= 10


class TestWebSearchSingleton:
    """Tests for singleton behavior."""

    def test_get_web_search_returns_same_instance(self):
        """Test that get_web_search returns the same instance."""
        reset_web_search()
        ws1 = get_web_search()
        ws2 = get_web_search()
        assert ws1 is ws2

    def test_reset_web_search_creates_new_instance(self):
        """Test that reset_web_search allows new instance creation."""
        reset_web_search()
        ws1 = get_web_search()
        reset_web_search()
        ws2 = get_web_search()
        assert ws1 is not ws2
