"""Test browser service functionality."""

import pytest
import sys
from unittest.mock import Mock, patch, AsyncMock, MagicMock


# Mock playwright module before importing BrowserService
sys.modules['playwright'] = MagicMock()
sys.modules['playwright.async_api'] = MagicMock()

from mcpbundles.services.browser import BrowserService


@pytest.mark.asyncio
async def test_browser_init():
    """Test browser service initialization."""
    service = BrowserService(headless=True, port=9223)
    
    assert service.headless is True
    assert service.port == 9223
    assert service.running is False
    assert service.playwright is None
    assert service.browser is None


@pytest.mark.asyncio
async def test_browser_start_headless():
    """Test starting browser in headless mode."""
    service = BrowserService(headless=True, port=9223)
    
    mock_playwright_instance = AsyncMock()
    mock_browser_instance = AsyncMock()
    
    # Mock the import within the start function
    with patch.dict('sys.modules', {
        'playwright': MagicMock(),
        'playwright.async_api': MagicMock(async_playwright=lambda: mock_playwright_instance)
    }):
        mock_playwright_instance.start = AsyncMock(return_value=mock_playwright_instance)
        mock_playwright_instance.chromium.launch = AsyncMock(return_value=mock_browser_instance)
        
        await service.start()
        
        assert service.running is True
        assert service.playwright is not None
        assert service.browser is not None


@pytest.mark.asyncio
async def test_browser_start_visible():
    """Test starting browser in visible mode."""
    service = BrowserService(headless=False, port=9223)
    
    mock_playwright_instance = AsyncMock()
    mock_browser_instance = AsyncMock()
    
    with patch.dict('sys.modules', {
        'playwright': MagicMock(),
        'playwright.async_api': MagicMock(async_playwright=lambda: mock_playwright_instance)
    }):
        mock_playwright_instance.start = AsyncMock(return_value=mock_playwright_instance)
        mock_playwright_instance.chromium.launch = AsyncMock(return_value=mock_browser_instance)
        
        await service.start()
        
        assert service.running is True


@pytest.mark.asyncio
async def test_browser_start_already_running():
    """Test starting browser when already running."""
    service = BrowserService(headless=False, port=9223)
    service.running = True
    
    # Mock a browser object that is connected
    mock_browser = AsyncMock()
    mock_browser.is_connected = Mock(return_value=True)
    service.browser = mock_browser
    
    # Mock the port check to return True
    service._is_port_open = Mock(return_value=True)
    
    # Should just return without doing anything
    await service.start()
    assert service.running is True


@pytest.mark.asyncio
async def test_browser_start_playwright_not_installed():
    """Test starting browser without playwright installed."""
    service = BrowserService(headless=False, port=9223)
    
    # Mock the import to raise ImportError
    def mock_import(*args, **kwargs):
        raise ImportError("No module named 'playwright'")
    
    with patch('builtins.__import__', side_effect=mock_import):
        with pytest.raises(ImportError):
            await service.start()


@pytest.mark.asyncio
async def test_browser_stop():
    """Test stopping browser."""
    service = BrowserService(headless=False, port=9223)
    
    # Setup mock browser
    mock_browser = AsyncMock()
    mock_playwright = AsyncMock()
    service.browser = mock_browser
    service.playwright = mock_playwright
    service.running = True
    
    await service.stop()
    
    assert service.running is False
    assert service.browser is None
    assert service.playwright is None
    mock_browser.close.assert_called_once()
    mock_playwright.stop.assert_called_once()


@pytest.mark.asyncio
async def test_browser_stop_not_running():
    """Test stopping browser when not running."""
    service = BrowserService(headless=False, port=9223)
    
    await service.stop()
    # Should not raise error


@pytest.mark.asyncio
async def test_browser_update_config_same():
    """Test updating config with same value."""
    service = BrowserService(headless=True, port=9223)
    service.running = True
    
    await service.update_config(headless=True)
    # Should not restart


@pytest.mark.asyncio
async def test_browser_update_config_change():
    """Test updating config with different value."""
    service = BrowserService(headless=True, port=9223)
    
    mock_playwright_instance = AsyncMock()
    mock_browser_instance = AsyncMock()
    
    with patch.dict('sys.modules', {
        'playwright': MagicMock(),
        'playwright.async_api': MagicMock(async_playwright=lambda: mock_playwright_instance)
    }):
        mock_playwright_instance.start = AsyncMock(return_value=mock_playwright_instance)
        mock_playwright_instance.chromium.launch = AsyncMock(return_value=mock_browser_instance)
        
        # Start browser first
        await service.start()
        assert service.headless is True
        
        # Update to visible mode
        await service.update_config(headless=False)
        
        assert service.headless is False


def test_browser_get_status_running():
    """Test getting status when running."""
    service = BrowserService(headless=False, port=9223)
    service.running = True
    
    status = service.get_status()
    
    assert status['enabled'] is True
    assert status['headless'] is False
    assert status['port'] == 9223
    assert status['mode'] == 'visible'


def test_browser_get_status_not_running():
    """Test getting status when not running."""
    service = BrowserService(headless=True, port=9223)
    service.running = False
    
    status = service.get_status()
    
    assert status['enabled'] is False
    assert status['headless'] is True
    assert status['mode'] == 'hidden'


@pytest.mark.asyncio
async def test_browser_cleanup_with_errors():
    """Test cleanup handles errors gracefully."""
    service = BrowserService(headless=False, port=9223)
    
    # Setup mocks that raise errors
    mock_browser = AsyncMock()
    mock_browser.close = AsyncMock(side_effect=Exception("Close error"))
    mock_playwright = AsyncMock()
    mock_playwright.stop = AsyncMock(side_effect=Exception("Stop error"))
    
    service.browser = mock_browser
    service.playwright = mock_playwright
    service.running = True
    
    # Should not raise exception
    await service.cleanup()
    
    assert service.running is False
    assert service.browser is None
    assert service.playwright is None

