from unittest.mock import AsyncMock

import pytest

from gsearch_mcp_cli.core.extractor import (
    NEWS_EXTRACT_JS,
    SEARCH_EXTRACT_JS,
    extract_ai_mode,
    extract_search_results,
)
from gsearch_mcp_cli.exceptions import SearchBlockedError
from gsearch_mcp_cli.models import AIResult, SearchResult


@pytest.mark.asyncio
async def test_extract_search_results_basic(mock_tab):
    mock_tab.evaluate = AsyncMock(side_effect=[
        False,  # blocked check
        {  # main extraction
            "ai_overview": {"text": "AI generated overview", "sources": []},
            "results": [
                {"position": 1, "title": "Result 1", "url": "https://example.com",
                 "snippet": "A snippet", "source": "example.com"},
                {"position": 2, "title": "Result 2", "url": "https://test.com",
                 "snippet": "Another snippet", "source": "test.com"},
            ],
            "people_also_ask": ["What is X?", "How does Y work?"],
            "related_searches": ["related query"],
        }
    ])

    result = await extract_search_results(mock_tab, "test query")
    assert isinstance(result, SearchResult)
    assert result.query == "test query"
    assert len(result.results) == 2
    assert result.results[0].title == "Result 1"
    assert result.ai_overview is not None
    assert result.ai_overview.text == "AI generated overview"
    assert len(result.people_also_ask) == 2


@pytest.mark.asyncio
async def test_extract_search_results_no_ai_overview(mock_tab):
    mock_tab.evaluate = AsyncMock(side_effect=[
        False,  # blocked check
        {
            "ai_overview": None,
            "results": [
                {"position": 1, "title": "Result", "url": "https://x.com",
                 "snippet": "Snippet", "source": "x.com"},
            ],
            "people_also_ask": [],
            "related_searches": [],
        }
    ])

    result = await extract_search_results(mock_tab, "q")
    assert result.ai_overview is None
    assert len(result.results) == 1


@pytest.mark.asyncio
async def test_extract_search_blocked(mock_tab):
    mock_tab.evaluate = AsyncMock(side_effect=[True])  # blocked = True

    with pytest.raises(SearchBlockedError):
        await extract_search_results(mock_tab, "q")


@pytest.mark.asyncio
async def test_extract_ai_mode_basic(mock_tab):
    mock_tab.evaluate = AsyncMock(side_effect=[
        False,  # blocked check
        {
            "synthesis": "Docker is an open-source platform...",
            "sections": [
                {"heading": "Core Concepts", "content": "Containers are...", "items": ["item1", "item2"]},
            ],
            "sources": [
                {"title": "Docker Docs", "url": "https://docs.docker.com", "domain": "docs.docker.com", "snippet": ""},
            ],
        }
    ])

    result = await extract_ai_mode(mock_tab, "what is docker")
    assert isinstance(result, AIResult)
    assert result.query == "what is docker"
    assert "Docker" in result.synthesis
    assert len(result.sections) == 1
    assert result.sections[0].heading == "Core Concepts"
    assert len(result.sources) == 1


@pytest.mark.asyncio
async def test_extract_search_results_news_type(mock_tab):
    """extract_search_results with search_type='news' uses NEWS_EXTRACT_JS."""
    mock_tab.evaluate = AsyncMock(side_effect=[
        False,  # blocked check
        {  # news extraction
            "ai_overview": None,
            "results": [
                {"position": 1, "title": "Breaking News Article",
                 "url": "https://reuters.com/article/1",
                 "snippet": "News snippet here", "source": "reuters.com"},
                {"position": 2, "title": "Another News Story",
                 "url": "https://bbc.com/news/2",
                 "snippet": "BBC snippet", "source": "bbc.com"},
            ],
            "people_also_ask": [],
            "related_searches": [],
        }
    ])

    result = await extract_search_results(mock_tab, "AI news", search_type="news")
    assert isinstance(result, SearchResult)
    assert len(result.results) == 2
    assert result.results[0].source == "reuters.com"
    assert result.ai_overview is None

    calls = mock_tab.evaluate.call_args_list
    assert calls[1][0][0] == NEWS_EXTRACT_JS


@pytest.mark.asyncio
async def test_extract_search_results_default_uses_standard_js(mock_tab):
    """extract_search_results with no search_type uses SEARCH_EXTRACT_JS."""
    mock_tab.evaluate = AsyncMock(side_effect=[
        False,
        {"ai_overview": None, "results": [], "people_also_ask": [], "related_searches": []},
    ])

    await extract_search_results(mock_tab, "test")
    calls = mock_tab.evaluate.call_args_list
    assert calls[1][0][0] == SEARCH_EXTRACT_JS


@pytest.mark.asyncio
async def test_extract_ai_mode_blocked(mock_tab):
    mock_tab.evaluate = AsyncMock(side_effect=[True])

    with pytest.raises(SearchBlockedError):
        await extract_ai_mode(mock_tab, "q")
