"""
双引擎模式 - requests + 浏览器智能切换

提供 HTTP 请求和浏览器操作的统一接口，根据页面特性自动选择最佳方式。

特性：
- 智能判断静态/动态页面，自动选择引擎
- Cookie 同步：requests 和浏览器间共享 Cookie
- 性能优化：静态页面用 requests，动态页面用浏览器

示例:
    async with EasyBrowser() as browser:
        engine = DualEngine(browser)

        # 智能请求（自动判断）
        response = await engine.get('https://example.com')

        # 强制使用 requests
        response = await engine.get('https://api.example.com/data', mode='requests')

        # 强制使用浏览器
        response = await engine.get('https://spa.example.com', mode='browser')

        # POST 请求
        response = await engine.post('https://api.example.com/login', json={'user': 'xxx'})
"""
import asyncio
from typing import Optional, Dict, Any, Union, TYPE_CHECKING
from dataclasses import dataclass
from loguru import logger
import re

if TYPE_CHECKING:
    from .browser import EasyBrowser
    from playwright.async_api import Page


@dataclass
class DualEngineResponse:
    """双引擎响应"""

    url: str
    status_code: int
    headers: Dict[str, str]
    content: bytes
    text: str
    json_data: Optional[Any]
    mode: str  # 'requests' 或 'browser'
    elapsed: float  # 请求耗时（秒）

    def json(self) -> Any:
        """获取 JSON 数据"""
        return self.json_data

    def __repr__(self) -> str:
        return f"<DualEngineResponse [{self.status_code}] mode={self.mode} url={self.url[:50]}...>"


class DualEngine:
    """
    双引擎模式

    结合 requests 和浏览器的优势：
    - requests: 快速、轻量，适合静态页面和 API
    - 浏览器: 支持 JavaScript，适合动态页面

    自动判断页面类型，选择最佳引擎。
    """

    # 静态资源扩展名（使用 requests 可能更快）
    STATIC_EXTENSIONS = {
        '.json', '.xml', '.rss', '.atom',
        '.txt', '.csv', '.html', '.htm',
    }

    # SPA（单页应用）特征
    SPA_INDICATORS = [
        '__NEXT_DATA__',           # Next.js
        '__NUXT__',                # Nuxt.js
        'window.__INITIAL_STATE__', # Redux
        'ng-version',              # Angular
        'data-reactroot',          # React
        'v-cloak',                 # Vue.js
        'data-v-',                 # Vue.js
    ]

    # 需要浏览器的 API 路径模式
    BROWSER_PATTERNS = [
        r'/api/v\d+/.*render',
        r'/api/v\d+/.*html',
        r'.*\.aspx',
        r'.*\.php\?.*session',
    ]

    def __init__(
        self,
        browser: Optional["EasyBrowser"] = None,
        requests_session: Optional[Any] = None,
        default_mode: str = "auto",
        static_threshold: float = 0.5,
    ):
        """
        初始化双引擎

        Args:
            browser: EasyBrowser 实例
            requests_session: requests.Session 实例（可选）
            default_mode: 默认模式 ('auto' | 'requests' | 'browser')
            static_threshold: 静态页面判断阈值（秒），请求时间小于此值可能为静态
        """
        self.browser = browser
        self._requests_session = requests_session
        self._default_mode = default_mode
        self._static_threshold = static_threshold

        # 缓存已检测的 URL 类型
        self._url_type_cache: Dict[str, str] = {}

    @property
    def requests_session(self):
        """获取或创建 requests Session"""
        if self._requests_session is None:
            try:
                import requests
                self._requests_session = requests.Session()
            except ImportError:
                raise ImportError("需要安装 requests: pip install requests")
        return self._requests_session

    @requests_session.setter
    def requests_session(self, session):
        """设置 requests Session"""
        self._requests_session = session

    # ==================== 请求方法 ====================

    async def get(
        self,
        url: str,
        mode: str = "auto",
        headers: Optional[Dict[str, str]] = None,
        cookies: Optional[Dict[str, str]] = None,
        timeout: float = 30.0,
        **kwargs,
    ) -> DualEngineResponse:
        """
        GET 请求

        Args:
            url: 请求 URL
            mode: 模式 ('auto' | 'requests' | 'browser')
            headers: 请求头
            cookies: Cookies
            timeout: 超时时间
            **kwargs: 其他参数

        Returns:
            DualEngineResponse: 响应对象
        """
        return await self._request(
            method="GET",
            url=url,
            mode=mode,
            headers=headers,
            cookies=cookies,
            timeout=timeout,
            **kwargs,
        )

    async def post(
        self,
        url: str,
        mode: str = "auto",
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
        cookies: Optional[Dict[str, str]] = None,
        timeout: float = 30.0,
        **kwargs,
    ) -> DualEngineResponse:
        """
        POST 请求

        Args:
            url: 请求 URL
            mode: 模式
            data: 表单数据
            json: JSON 数据
            headers: 请求头
            cookies: Cookies
            timeout: 超时时间

        Returns:
            DualEngineResponse: 响应对象
        """
        return await self._request(
            method="POST",
            url=url,
            mode=mode,
            data=data,
            json=json,
            headers=headers,
            cookies=cookies,
            timeout=timeout,
            **kwargs,
        )

    async def put(
        self,
        url: str,
        mode: str = "auto",
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
        timeout: float = 30.0,
        **kwargs,
    ) -> DualEngineResponse:
        """PUT 请求"""
        return await self._request(
            method="PUT",
            url=url,
            mode=mode,
            data=data,
            json=json,
            headers=headers,
            timeout=timeout,
            **kwargs,
        )

    async def delete(
        self,
        url: str,
        mode: str = "auto",
        headers: Optional[Dict[str, str]] = None,
        timeout: float = 30.0,
        **kwargs,
    ) -> DualEngineResponse:
        """DELETE 请求"""
        return await self._request(
            method="DELETE",
            url=url,
            mode=mode,
            headers=headers,
            timeout=timeout,
            **kwargs,
        )

    # ==================== 核心请求方法 ====================

    async def _request(
        self,
        method: str,
        url: str,
        mode: str = "auto",
        **kwargs,
    ) -> DualEngineResponse:
        """
        统一请求方法

        Args:
            method: HTTP 方法
            url: 请求 URL
            mode: 模式 ('auto' | 'requests' | 'browser')
            **kwargs: 其他参数

        Returns:
            DualEngineResponse: 响应对象
        """
        # 确定使用的模式
        actual_mode = mode if mode != "auto" else self._determine_mode(url)

        logger.debug(f"双引擎请求: {method} {url} (mode={actual_mode})")

        if actual_mode == "requests":
            return await self._request_with_requests(method, url, **kwargs)
        else:
            return await self._request_with_browser(method, url, **kwargs)

    async def _request_with_requests(
        self,
        method: str,
        url: str,
        headers: Optional[Dict[str, str]] = None,
        cookies: Optional[Dict[str, str]] = None,
        timeout: float = 30.0,
        **kwargs,
    ) -> DualEngineResponse:
        """使用 requests 发送请求"""
        import time

        start_time = time.time()

        try:
            response = await asyncio.to_thread(
                self.requests_session.request,
                method=method,
                url=url,
                headers=headers,
                cookies=cookies,
                timeout=timeout,
                **kwargs,
            )

            elapsed = time.time() - start_time

            # 尝试解析 JSON
            json_data = None
            try:
                json_data = response.json()
            except:
                pass

            return DualEngineResponse(
                url=url,
                status_code=response.status_code,
                headers=dict(response.headers),
                content=response.content,
                text=response.text,
                json_data=json_data,
                mode="requests",
                elapsed=elapsed,
            )
        except Exception as e:
            logger.error(f"requests 请求失败: {e}")
            raise

    async def _request_with_browser(
        self,
        method: str,
        url: str,
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
        cookies: Optional[Dict[str, str]] = None,
        timeout: float = 30.0,
        **kwargs,
    ) -> DualEngineResponse:
        """使用浏览器发送请求"""
        import time

        if not self.browser:
            raise RuntimeError("浏览器未初始化，无法使用浏览器模式")

        start_time = time.time()

        try:
            page = self.browser.page

            # 同步 cookies
            if cookies:
                await self.browser.cookies.set_cookies(cookies)

            if method == "GET":
                response = await page.goto(url, timeout=timeout * 1000)
            else:
                # POST/PUT/DELETE 使用 page.request
                response = await page.request.fetch(
                    url,
                    method=method,
                    headers=headers,
                    data=data,
                    post_data=json,
                )

            elapsed = time.time() - start_time

            # 获取页面内容
            content = await page.content()
            text = await page.inner_text("body")

            # 尝试解析 JSON
            json_data = None
            try:
                import json
                json_data = json.loads(text)
            except:
                pass

            return DualEngineResponse(
                url=url,
                status_code=response.status if response else 200,
                headers=dict(response.headers) if response else {},
                content=content.encode(),
                text=text,
                json_data=json_data,
                mode="browser",
                elapsed=elapsed,
            )
        except Exception as e:
            logger.error(f"浏览器请求失败: {e}")
            raise

    # ==================== 智能判断 ====================

    def _determine_mode(self, url: str) -> str:
        """
        智能判断应该使用的模式

        Args:
            url: 请求 URL

        Returns:
            str: 'requests' 或 'browser'
        """
        # 检查缓存
        if url in self._url_type_cache:
            return self._url_type_cache[url]

        # 检查 URL 特征
        url_lower = url.lower()

        # 1. 静态资源扩展名
        for ext in self.STATIC_EXTENSIONS:
            if url_lower.endswith(ext):
                self._url_type_cache[url] = "requests"
                return "requests"

        # 2. API 端点（通常是 JSON）
        if "/api/" in url_lower or url_lower.endswith(".json"):
            self._url_type_cache[url] = "requests"
            return "requests"

        # 3. 需要浏览器的模式
        for pattern in self.BROWSER_PATTERNS:
            if re.search(pattern, url_lower):
                self._url_type_cache[url] = "browser"
                return "browser"

        # 4. 检查是否是已知 SPA 路径
        spa_paths = ['/app', '/dashboard', '/admin', '/portal', '/workspace']
        for path in spa_paths:
            if path in url_lower:
                self._url_type_cache[url] = "browser"
                return "browser"

        # 5. 默认使用 requests（更快）
        # 如果失败或内容需要 JS，再切换到浏览器
        self._url_type_cache[url] = "requests"
        return "requests"

    async def is_static_page(self, url: str) -> bool:
        """
        检测页面是否为静态页面

        通过请求页面并检查内容来判断。

        Args:
            url: 页面 URL

        Returns:
            bool: 是否为静态页面
        """
        # 先用 requests 快速请求
        try:
            response = await self.get(url, mode="requests", timeout=10.0)
            content = response.text

            # 检查 SPA 指示器
            for indicator in self.SPA_INDICATORS:
                if indicator in content:
                    return False

            # 检查是否有 noscript 标签（通常表示需要 JS）
            if "<noscript>" in content and "JavaScript" in content:
                return False

            return True
        except:
            # 如果 requests 失败，假设需要浏览器
            return False

    # ==================== Cookie 同步 ====================

    async def sync_cookies_to_browser(self) -> None:
        """
        将 requests 的 Cookie 同步到浏览器
        """
        if not self.browser or not self._requests_session:
            return

        cookies = self._requests_session.cookies.get_dict()
        if cookies:
            await self.browser.cookies.set_cookies(cookies)
            logger.debug(f"已同步 {len(cookies)} 个 Cookie 到浏览器")

    async def sync_cookies_to_requests(self) -> None:
        """
        将浏览器的 Cookie 同步到 requests
        """
        if not self.browser or not self._requests_session:
            return

        cookies = await self.browser.cookies.get_all()
        for cookie in cookies:
            self._requests_session.cookies.set(
                cookie.get('name', ''),
                cookie.get('value', ''),
                domain=cookie.get('domain', ''),
                path=cookie.get('path', '/'),
            )
        logger.debug(f"已同步 {len(cookies)} 个 Cookie 到 requests")

    async def sync_cookies(self, direction: str = "both") -> None:
        """
        同步 Cookie

        Args:
            direction: 同步方向 ('to_browser' | 'to_requests' | 'both')
        """
        if direction == "to_browser":
            await self.sync_cookies_to_browser()
        elif direction == "to_requests":
            await self.sync_cookies_to_requests()
        else:
            await self.sync_cookies_to_browser()
            await self.sync_cookies_to_requests()

    # ==================== 工具方法 ====================

    def clear_cache(self) -> None:
        """清空 URL 类型缓存"""
        self._url_type_cache.clear()

    def set_url_type(self, url: str, mode: str) -> None:
        """
        手动设置 URL 类型

        Args:
            url: URL
            mode: 'requests' 或 'browser'
        """
        self._url_type_cache[url] = mode

    def get_stats(self) -> Dict[str, Any]:
        """获取双引擎统计信息"""
        return {
            "cached_urls": len(self._url_type_cache),
            "requests_mode_count": sum(1 for v in self._url_type_cache.values() if v == "requests"),
            "browser_mode_count": sum(1 for v in self._url_type_cache.values() if v == "browser"),
            "has_browser": self.browser is not None,
            "has_requests_session": self._requests_session is not None,
        }


class DualEngineContext:
    """
    双引擎上下文管理器

    自动管理浏览器和 requests Session 的生命周期。

    Example:
        async with DualEngineContext(config) as engine:
            response = await engine.get('https://example.com')
    """

    def __init__(
        self,
        config: Optional["Config"] = None,
        default_mode: str = "auto",
    ):
        self.config = config
        self.default_mode = default_mode
        self._browser = None
        self._engine = None

    async def __aenter__(self) -> DualEngine:
        from .browser import EasyBrowser

        # 启动浏览器
        self._browser = EasyBrowser(self.config)
        await self._browser.start()

        # 创建双引擎
        self._engine = DualEngine(
            browser=self._browser,
            default_mode=self.default_mode,
        )

        return self._engine

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self._browser:
            await self._browser.close()
