import pytest
from unittest.mock import AsyncMock, MagicMock, patch
import aiohttp
from tools.web_scraper import WebScraper

@pytest.fixture
def mock_session():
    """Create a mock aiohttp session."""
    mock = MagicMock()
    mock_response = AsyncMock()
    mock_response.status = 200
    mock_response.text = AsyncMock(return_value="<html><body><h1>Test</h1></body></html>")
    
    mock_context = AsyncMock()
    mock_context.__aenter__.return_value = mock_response
    mock.get.return_value = mock_context
    mock.close = AsyncMock()
    
    return mock

@pytest.fixture
def scraper(mock_session):
    """Create a WebScraper instance with a mock session."""
    scraper = WebScraper()
    scraper.session = mock_session
    return scraper

@pytest.mark.asyncio
async def test_fetch_page(scraper, mock_session):
    """Test fetching a single page."""
    content = await scraper.fetch_page("https://example.com")
    assert "Test" in content
    mock_session.get.assert_called_once_with("https://example.com")

@pytest.mark.asyncio
async def test_process_urls(scraper, mock_session):
    """Test processing multiple URLs concurrently."""
    urls = ["https://example1.com", "https://example2.com"]
    results = await scraper.process_urls(urls, max_concurrent=2)
    assert len(results) == len(urls)
    assert all("Test" in content for content in results.values())
    assert mock_session.get.call_count == len(urls)
    mock_session.close.assert_awaited_once()

@pytest.mark.asyncio
async def test_fetch_page_error(scraper, mock_session):
    """Test error handling when fetching a page."""
    mock_response = AsyncMock()
    mock_response.status = 404
    mock_context = AsyncMock()
    mock_context.__aenter__.return_value = mock_response
    mock_session.get.return_value = mock_context
    
    content = await scraper.fetch_page("https://example.com")
    assert content == ""
    mock_session.get.assert_called_once_with("https://example.com")

@pytest.mark.asyncio
async def test_fetch_page_exception(scraper, mock_session):
    """Test handling of exceptions during page fetch."""
    mock_session.get.side_effect = aiohttp.ClientError("Connection error")
    
    content = await scraper.fetch_page("https://example.com")
    assert content == ""
    mock_session.get.assert_called_once_with("https://example.com")

@pytest.mark.asyncio
async def test_process_urls_with_errors(scraper, mock_session):
    """Test processing URLs with some failures."""
    urls = ["https://example1.com", "https://example2.com"]
    
    # First URL succeeds
    mock_success = AsyncMock()
    mock_success.status = 200
    mock_success.text = AsyncMock(return_value="<html><body><h1>Test</h1></body></html>")
    mock_success_context = AsyncMock()
    mock_success_context.__aenter__.return_value = mock_success
    
    # Second URL fails
    mock_failure = AsyncMock()
    mock_failure.status = 404
    mock_failure_context = AsyncMock()
    mock_failure_context.__aenter__.return_value = mock_failure
    
    mock_session.get.side_effect = [mock_success_context, mock_failure_context]
    
    results = await scraper.process_urls(urls, max_concurrent=2)
    assert len(results) == len(urls)
    assert "Test" in results[urls[0]]
    assert results[urls[1]] == ""
    mock_session.close.assert_awaited_once()

@pytest.mark.asyncio
async def test_process_urls_max_concurrent(scraper, mock_session):
    """Test respecting max_concurrent limit."""
    urls = ["https://example1.com", "https://example2.com", "https://example3.com"]
    max_concurrent = 2
    
    results = await scraper.process_urls(urls, max_concurrent=max_concurrent)
    assert len(results) == len(urls)
    assert all("Test" in content for content in results.values())
    assert mock_session.get.call_count == len(urls)
    mock_session.close.assert_awaited_once()

@pytest.mark.asyncio
async def test_parse_html(scraper):
    """Test HTML parsing functionality."""
    # Test with empty input
    assert scraper._parse_html("") == ""
    
    # Test with simple HTML
    html = """
    <html>
        <body>
            <h1>Title</h1>
            <p>Paragraph text</p>
            <script>var x = 1;</script>
            <style>.css { color: red; }</style>
        </body>
    </html>
    """
    result = scraper._parse_html(html)
    assert "Title" in result
    assert "Paragraph text" in result
    assert "var x = 1" not in result  # Script content should be removed
    assert ".css" not in result  # Style content should be removed
    
    # Test with nested elements
    html = """
    <html>
        <body>
            <div>
                <p>Level 1</p>
                <div>
                    <p>Level 2</p>
                </div>
            </div>
        </body>
    </html>
    """
    result = scraper._parse_html(html)
    assert "Level 1" in result
    assert "Level 2" in result 