"""Tests for linkedin_mcp_server.drivers.browser singleton lifecycle."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from linkedin_mcp_server.config.schema import AppConfig
from linkedin_mcp_server.drivers.browser import (
    get_or_create_browser,
    reset_browser_for_testing,
)


@pytest.fixture(autouse=True)
def _reset_browser():
    """Ensure clean singleton state for each test."""
    reset_browser_for_testing()
    yield
    reset_browser_for_testing()


@pytest.fixture(autouse=True)
def _mock_config(monkeypatch, tmp_path):
    """Provide a test config so get_config() never triggers argparse."""
    config = AppConfig()
    config.browser.user_data_dir = str(tmp_path / "profile")
    monkeypatch.setattr(
        "linkedin_mcp_server.drivers.browser.get_config", lambda: config
    )


def _make_mock_browser(*, logged_in: bool = True) -> MagicMock:
    """Create a mock BrowserManager with controllable login state."""
    browser = MagicMock()
    browser.start = AsyncMock()
    browser.close = AsyncMock()
    browser.page = MagicMock()
    browser.page.goto = AsyncMock()
    browser.page.set_default_timeout = MagicMock()
    browser.import_cookies = AsyncMock(return_value=False)
    browser.export_cookies = AsyncMock(return_value=False)
    return browser


@pytest.mark.asyncio
async def test_get_or_create_browser_auth_success(monkeypatch):
    """Successful auth assigns singleton and returns browser."""
    mock_browser = _make_mock_browser()

    with (
        patch(
            "linkedin_mcp_server.drivers.browser.BrowserManager",
            return_value=mock_browser,
        ),
        patch(
            "linkedin_mcp_server.drivers.browser.is_logged_in",
            new_callable=AsyncMock,
            return_value=True,
        ),
    ):
        result = await get_or_create_browser()

    assert result is mock_browser
    mock_browser.start.assert_awaited_once()
    mock_browser.page.goto.assert_awaited_once()


@pytest.mark.asyncio
async def test_get_or_create_browser_auth_failure_cleans_up(monkeypatch):
    """Failed auth closes browser and does NOT assign singleton."""
    from linkedin_mcp_server.core import AuthenticationError

    mock_browser = _make_mock_browser()

    with (
        patch(
            "linkedin_mcp_server.drivers.browser.BrowserManager",
            return_value=mock_browser,
        ),
        patch(
            "linkedin_mcp_server.drivers.browser.is_logged_in",
            new_callable=AsyncMock,
            return_value=False,
        ),
        pytest.raises(AuthenticationError),
    ):
        await get_or_create_browser()

    # Browser must be closed on failure
    mock_browser.close.assert_awaited_once()

    # Singleton must NOT be set — next call should create fresh browser
    from linkedin_mcp_server.drivers.browser import _browser

    assert _browser is None


@pytest.mark.asyncio
async def test_singleton_returns_existing_browser(monkeypatch):
    """Second call returns the same browser instance (singleton)."""
    mock_browser = _make_mock_browser()

    with (
        patch(
            "linkedin_mcp_server.drivers.browser.BrowserManager",
            return_value=mock_browser,
        ) as ctor,
        patch(
            "linkedin_mcp_server.drivers.browser.is_logged_in",
            new_callable=AsyncMock,
            return_value=True,
        ),
    ):
        first = await get_or_create_browser()
        second = await get_or_create_browser()

    assert first is second
    # Constructor should only be called once
    ctor.assert_called_once()
