"""Shared backend — single browser instance with lazy init and cleanup."""

from __future__ import annotations

import asyncio
import logging
import subprocess
from urllib.parse import urlencode

from gsearch_mcp_cli.constants import (
    CHROME_PROFILE_DIR,
    DEFAULT_BURST,
    DEFAULT_RATE_LIMIT,
    GOOGLE_SEARCH_URL,
    TIMEOUT_AI_MODE,
    TIMEOUT_FETCH_BROWSER,
    TIMEOUT_SEARCH,
)
from gsearch_mcp_cli.core.browser import launch_chrome, shutdown_chrome
from gsearch_mcp_cli.core.cdp import BrowserCDP
from gsearch_mcp_cli.core.extractor import extract_ai_mode, extract_search_results
from gsearch_mcp_cli.core.fetcher import fetch_page, validate_url
from gsearch_mcp_cli.core.rate_limiter import RateLimiter
from gsearch_mcp_cli.core.warmup import mark_warmed_up, needs_warmup, warmup_profile
from gsearch_mcp_cli.exceptions import SearchBlockedError
from gsearch_mcp_cli.models import AIResult, FetchResult, SearchResult

logger = logging.getLogger(__name__)

_browser_cdp: BrowserCDP | None = None
_chrome_proc: subprocess.Popen | None = None
_rate_limiter: RateLimiter | None = None
_init_lock: asyncio.Lock | None = None


def _get_lock() -> asyncio.Lock:
    global _init_lock
    if _init_lock is None:
        _init_lock = asyncio.Lock()
    return _init_lock


def _get_rate_limiter() -> RateLimiter:
    global _rate_limiter
    if _rate_limiter is None:
        _rate_limiter = RateLimiter(rate=DEFAULT_RATE_LIMIT, burst=DEFAULT_BURST)
    return _rate_limiter


async def _get_browser() -> BrowserCDP:
    """Lazy-init browser with double-check locking."""
    global _browser_cdp, _chrome_proc

    if _browser_cdp is not None and await _browser_cdp.is_healthy():
        return _browser_cdp

    async with _get_lock():
        if _browser_cdp is not None and await _browser_cdp.is_healthy():
            return _browser_cdp

        if _chrome_proc is not None:
            shutdown_chrome(_chrome_proc)
            _chrome_proc = None
        if _browser_cdp is not None:
            try:
                await _browser_cdp.close()
            except Exception:
                pass
            _browser_cdp = None

        proc, port = await launch_chrome()
        _chrome_proc = proc

        cdp = BrowserCDP()
        await cdp.connect(port)
        _browser_cdp = cdp

        if needs_warmup(CHROME_PROFILE_DIR):
            logger.info("Profile needs warmup, running warmup sequence")
            await warmup_profile(_browser_cdp)
            mark_warmed_up(CHROME_PROFILE_DIR)

        return _browser_cdp


SEARCH_TYPE_MAP = {
    "all": {},
    "news": {"tbm": "nws"},
    "images": {"tbm": "isch"},
    "videos": {"tbm": "vid"},
    "shopping": {"tbm": "shop"},
    "books": {"tbm": "bks"},
    "forums": {"udm": "18"},
    "short_videos": {"udm": "44"},
}

TIME_FILTER_MAP = {
    "hour": "qdr:h",
    "12h": "qdr:h12",
    "day": "qdr:d",
    "week": "qdr:w",
    "month": "qdr:m",
    "year": "qdr:y",
}

# Aliases for convenience
TIME_FILTER_ALIASES = {
    "1h": "hour",
    "12h": "12h",
    "24h": "day",
    "1d": "day",
    "7d": "week",
    "1w": "week",
    "1m": "month",
    "1y": "year",
}


def _build_tbs_param(
    time_filter: str | None = None,
    date_from: str | None = None,
    date_to: str | None = None,
) -> str | None:
    """Build Google's tbs parameter for date filtering.

    time_filter: "hour"/"1h", "12h", "day"/"24h", "week"/"7d", "month", "year"
                 Also supports "Nh" for N hours (e.g., "6h" = last 6 hours)
    date_from/date_to: "YYYY-MM-DD" format for custom range
    """
    if time_filter:
        resolved = TIME_FILTER_ALIASES.get(time_filter, time_filter)
        if resolved in TIME_FILTER_MAP:
            return TIME_FILTER_MAP[resolved]
        # Support arbitrary "Nh" patterns (e.g., "6h" -> qdr:h6)
        import re
        match = re.match(r"^(\d+)h$", time_filter)
        if match:
            return f"qdr:h{match.group(1)}"
    if date_from or date_to:
        from_str = date_from.replace("-", "/") if date_from else ""
        to_str = date_to.replace("-", "/") if date_to else ""
        return f"cdr:1,cd_min:{from_str},cd_max:{to_str}"
    return None


def _build_search_url(
    query: str,
    language: str = "en",
    region: str | None = None,
    max_results: int = 10,
    time_filter: str | None = None,
    date_from: str | None = None,
    date_to: str | None = None,
    search_type: str | None = None,
) -> str:
    params: dict[str, str | int] = {"q": query, "hl": language, "num": max_results}
    if region:
        params["gl"] = region
    tbs = _build_tbs_param(time_filter, date_from, date_to)
    if tbs:
        params["tbs"] = tbs
    if search_type and search_type in SEARCH_TYPE_MAP:
        params.update(SEARCH_TYPE_MAP[search_type])
    return f"{GOOGLE_SEARCH_URL}?{urlencode(params)}"


def _build_ai_mode_url(
    query: str,
    language: str = "en",
    region: str | None = None,
    time_filter: str | None = None,
    date_from: str | None = None,
    date_to: str | None = None,
) -> str:
    params: dict[str, str | int] = {"q": query, "hl": language, "udm": 50}
    if region:
        params["gl"] = region
    tbs = _build_tbs_param(time_filter, date_from, date_to)
    if tbs:
        params["tbs"] = tbs
    return f"{GOOGLE_SEARCH_URL}?{urlencode(params)}"


async def search(
    query: str,
    max_results: int = 10,
    include_ai: bool = True,
    language: str = "en",
    region: str | None = None,
    search_type: str | None = None,
    time_filter: str | None = None,
    date_from: str | None = None,
    date_to: str | None = None,
    timeout: float = TIMEOUT_SEARCH,
) -> SearchResult:
    """Run a Google search and return structured results.

    Args:
        search_type: Search vertical — "news", "images", "videos", "shopping",
                     "books", "forums", "short_videos"
        time_filter: Quick filter — "hour", "day", "week", "month", "year"
        date_from: Custom range start — "YYYY-MM-DD"
        date_to: Custom range end — "YYYY-MM-DD"
    """
    await _get_rate_limiter().acquire()
    browser = await _get_browser()
    url = _build_search_url(
        query, language=language, region=region, max_results=max_results,
        time_filter=time_filter, date_from=date_from, date_to=date_to,
        search_type=search_type,
    )

    async with browser.create_tab() as tab:
        await tab.navigate(url)
        await asyncio.sleep(3)
        try:
            return await extract_search_results(tab, query, search_type=search_type)
        except SearchBlockedError:
            logger.warning("Search blocked, attempting warmup and retry")
            await warmup_profile(browser)
            mark_warmed_up(CHROME_PROFILE_DIR)

    async with browser.create_tab() as tab:
        await tab.navigate(url)
        await asyncio.sleep(3)
        return await extract_search_results(tab, query, search_type=search_type)


async def ai_search(
    query: str,
    language: str = "en",
    region: str | None = None,
    time_filter: str | None = None,
    date_from: str | None = None,
    date_to: str | None = None,
    timeout: float = TIMEOUT_AI_MODE,
) -> AIResult:
    """Run a Google AI Mode search and return structured results.

    Args:
        time_filter: Quick filter — "hour", "day", "week", "month", "year"
        date_from: Custom range start — "YYYY-MM-DD"
        date_to: Custom range end — "YYYY-MM-DD"
    """
    await _get_rate_limiter().acquire()
    browser = await _get_browser()
    url = _build_ai_mode_url(
        query, language=language, region=region,
        time_filter=time_filter, date_from=date_from, date_to=date_to,
    )

    async with browser.create_tab() as tab:
        await tab.navigate(url)
        await asyncio.sleep(6)
        try:
            return await extract_ai_mode(tab, query)
        except SearchBlockedError:
            logger.warning("AI search blocked, attempting warmup and retry")
            await warmup_profile(browser)
            mark_warmed_up(CHROME_PROFILE_DIR)

    async with browser.create_tab() as tab:
        await tab.navigate(url)
        await asyncio.sleep(6)
        return await extract_ai_mode(tab, query)


async def fetch(
    url: str,
    format: str = "md",
    use_browser: str = "auto",
    timeout: float = TIMEOUT_FETCH_BROWSER,
) -> FetchResult:
    """Fetch a URL and return clean content."""
    validated_url = validate_url(url)
    browser_cdp = None
    if use_browser != "never":
        try:
            browser_cdp = await _get_browser()
        except Exception:
            logger.debug("Browser unavailable for fetch, falling back to HTTP")
    return await fetch_page(
        validated_url,
        format=format,
        use_browser=use_browser,
        browser_cdp=browser_cdp,
        timeout=timeout,
    )


async def shutdown() -> None:
    """Shut down browser and reset all module-level state."""
    global _browser_cdp, _chrome_proc, _rate_limiter, _init_lock

    if _browser_cdp is not None:
        try:
            await _browser_cdp.close()
        except Exception:
            pass
        _browser_cdp = None

    if _chrome_proc is not None:
        shutdown_chrome(_chrome_proc)
        _chrome_proc = None

    _rate_limiter = None
    _init_lock = None
