"""
Patchright browser management for LinkedIn scraping.

Provides async browser lifecycle management using BrowserManager with persistent
context. Implements a singleton pattern for browser reuse across tool calls with
automatic profile persistence.
"""

import logging
import shutil
import tempfile
from pathlib import Path

from linkedin_mcp_server.core import (
    AuthenticationError,
    BrowserManager,
    detect_rate_limit,
    is_logged_in,
)

from linkedin_mcp_server.config import get_config

logger = logging.getLogger(__name__)


# Default persistent profile directory
DEFAULT_PROFILE_DIR = Path.home() / ".linkedin-mcp" / "profile"

# Global browser instance (singleton)
_browser: BrowserManager | None = None
_headless: bool = True


def _apply_browser_settings(browser: BrowserManager) -> None:
    """Apply configuration settings to browser instance."""
    config = get_config()
    browser.page.set_default_timeout(config.browser.default_timeout)


async def get_or_create_browser(
    headless: bool | None = None,
) -> BrowserManager:
    """
    Get existing browser or create and initialize a new one.

    Uses a singleton pattern to reuse the browser across tool calls.
    Uses persistent context for automatic profile persistence.

    Args:
        headless: Run browser in headless mode. Defaults to config value.

    Returns:
        Initialized BrowserManager instance

    Raises:
        AuthenticationError: If no valid authentication found
    """
    global _browser, _headless

    if headless is not None:
        _headless = headless

    if _browser is not None:
        return _browser

    config = get_config()
    user_data_dir = Path(config.browser.user_data_dir).expanduser()
    viewport = {
        "width": config.browser.viewport_width,
        "height": config.browser.viewport_height,
    }

    # Build launch options for custom browser path
    launch_options: dict[str, str] = {}
    if config.browser.chrome_path:
        launch_options["executable_path"] = config.browser.chrome_path
        logger.info("Using custom Chrome path: %s", config.browser.chrome_path)

    logger.info(
        "Creating new browser (headless=%s, slow_mo=%sms, viewport=%sx%s, profile=%s)",
        _headless,
        config.browser.slow_mo,
        viewport["width"],
        viewport["height"],
        user_data_dir,
    )
    browser = BrowserManager(
        user_data_dir=user_data_dir,
        headless=_headless,
        slow_mo=config.browser.slow_mo,
        user_agent=config.browser.user_agent,
        viewport=viewport,
        **launch_options,
    )
    await browser.start()

    # Navigate to LinkedIn to check authentication
    await browser.page.goto("https://www.linkedin.com/feed/")
    if await is_logged_in(browser.page):
        _apply_browser_settings(browser)
        _browser = browser  # Assign only after auth succeeds
        return _browser

    # Native auth failed — try the cross-platform cookie bridge.
    # On macOS→Linux, Chromium can't decrypt macOS-encrypted cookies in the
    # persistent profile. We copy the profile to a temp dir (so the original
    # isn't corrupted by Linux Chromium writing back), remove the undecryptable
    # Cookies DB, and inject auth cookies from the portable JSON file.
    cookie_path = user_data_dir.parent / "cookies.json"
    if cookie_path.exists():
        logger.info("Native auth failed, attempting cross-platform cookie bridge...")
        await browser.close()

        # Copy profile to temp dir — protects the macOS original
        temp_dir = Path(tempfile.mkdtemp(prefix="linkedin-mcp-"))
        temp_profile = temp_dir / "profile"
        shutil.copytree(user_data_dir, temp_profile)

        # Remove encrypted Cookies DB (can't be decrypted cross-platform)
        (temp_profile / "Default" / "Cookies").unlink(missing_ok=True)
        (temp_profile / "Default" / "Cookies-journal").unlink(missing_ok=True)

        browser = BrowserManager(
            user_data_dir=temp_profile,
            headless=_headless,
            slow_mo=config.browser.slow_mo,
            user_agent=config.browser.user_agent,
            viewport=viewport,
            **launch_options,
        )
        await browser.start()

        # First nav establishes session cookies (bcookie, JSESSIONID, etc.)
        await browser.page.goto("https://www.linkedin.com/feed/")
        # Import auth cookies (li_at, li_rm) from the portable file
        if await browser.import_cookies(cookie_path):
            await browser.page.goto("https://www.linkedin.com/feed/")
            if await is_logged_in(browser.page):
                logger.info("Authentication recovered via portable cookies")
                _apply_browser_settings(browser)
                _browser = browser
                return _browser

    # Auth failed — clean up and fail fast
    await browser.close()
    raise AuthenticationError(
        "No authentication found. Run with --login to create a profile."
    )


async def close_browser() -> None:
    """Close the browser and cleanup resources."""
    global _browser

    if _browser is not None:
        logger.info("Closing browser...")
        # Export cookies before closing to keep portable file fresh
        try:
            await _browser.export_cookies()
        except Exception:
            logger.debug("Cookie export on close skipped", exc_info=True)
        await _browser.close()
        _browser = None
        logger.info("Browser closed")


def get_profile_dir() -> Path:
    """Get the resolved profile directory from config."""
    config = get_config()
    return Path(config.browser.user_data_dir).expanduser()


def profile_exists(profile_dir: Path | None = None) -> bool:
    """Check if a persistent browser profile exists and is non-empty."""
    if profile_dir is None:
        profile_dir = get_profile_dir()
    return profile_dir.is_dir() and any(profile_dir.iterdir())


def set_headless(headless: bool) -> None:
    """Set headless mode for future browser creation."""
    global _headless
    _headless = headless


async def validate_session() -> bool:
    """
    Check if the current session is still valid (logged in).

    Returns:
        True if session is valid and user is logged in
    """
    browser = await get_or_create_browser()
    return await is_logged_in(browser.page)


async def ensure_authenticated() -> None:
    """
    Validate session and raise if expired.

    Raises:
        AuthenticationError: If session is expired or invalid
    """
    if not await validate_session():
        raise AuthenticationError("Session expired or invalid.")


async def check_rate_limit() -> None:
    """
    Proactively check for rate limiting.

    Should be called after navigation to detect if LinkedIn is blocking requests.

    Raises:
        RateLimitError: If rate limiting is detected
    """
    browser = await get_or_create_browser()
    await detect_rate_limit(browser.page)


def reset_browser_for_testing() -> None:
    """Reset global browser state for test isolation."""
    global _browser, _headless
    _browser = None
    _headless = True
