"""
页面基类（异步版本）
提供所有页面对象的通用方法，支持丰富的配置选项

注意：使用 Playwright 异步 API (async_api)
"""
import asyncio
import time
import traceback
import typing
from typing import Optional, Callable, Any, List, Union
from enum import Enum
from functools import wraps
from playwright.async_api import Page, Locator
from executor.logger import logger


# 默认配置常量
DEFAULT_TIMEOUT = 15000  # 默认超时时间（毫秒）


class ErrorStrategy(Enum):
    """异常处理策略"""
    RAISE = "raise"      # 抛出异常
    IGNORE = "ignore"    # 忽略异常
    RETRY = "retry"      # 重试


class ActionConfig:
    """操作配置类"""
    
    def __init__(
        self,
        timeout: Optional[float] = DEFAULT_TIMEOUT / 1000,           # 超时时间（秒）
        wait_before: float = 0,                     # 执行前等待（秒）
        wait_after: float = 0,                      # 执行后等待（秒）
        error_strategy: ErrorStrategy = ErrorStrategy.RAISE,  # 异常策略
        retry_times: int = 2,                       # 重试次数
        retry_interval: float = 1.0,                # 重试间隔（秒）
        ignore_exceptions: tuple = (Exception,),    # 忽略的异常类型
    ):
        self.timeout = timeout
        self.wait_before = wait_before
        self.wait_after = wait_after
        self.error_strategy = error_strategy
        self.retry_times = retry_times
        self.retry_interval = retry_interval
        self.ignore_exceptions = ignore_exceptions
    
    def merge(self, **kwargs) -> 'ActionConfig':
        """合并配置，返回新的配置对象"""
        params = {
            'timeout': kwargs.get('timeout', self.timeout),
            'wait_before': kwargs.get('wait_before', self.wait_before),
            'wait_after': kwargs.get('wait_after', self.wait_after),
            'error_strategy': kwargs.get('error_strategy', self.error_strategy),
            'retry_times': kwargs.get('retry_times', self.retry_times),
            'retry_interval': kwargs.get('retry_interval', self.retry_interval),
            'ignore_exceptions': kwargs.get('ignore_exceptions', self.ignore_exceptions),
        }
        return ActionConfig(**params)


def action_wrapper(action_name: str = None):
    """
    操作包装器装饰器（异步版本）
    自动处理日志、等待、异常、重试等通用逻辑
    
    Args:
        action_name: 操作名称（用于日志）
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(self, *args, **kwargs):
            # 提取配置参数
            timeout = kwargs.pop('timeout', None)
            wait_before = kwargs.pop('wait_before', None)
            wait_after = kwargs.pop('wait_after', None)
            error_strategy = kwargs.pop('error_strategy', None)
            retry_times = kwargs.pop('retry_times', None)
            retry_interval = kwargs.pop('retry_interval', None)
            description = kwargs.pop('description', None)
            
            # 合并配置
            cfg = self.default_config.merge(
                timeout=timeout,
                wait_before=wait_before if wait_before is not None else self.default_config.wait_before,
                wait_after=wait_after if wait_after is not None else self.default_config.wait_after,
                error_strategy=error_strategy or self.default_config.error_strategy,
                retry_times=retry_times if retry_times is not None else self.default_config.retry_times,
                retry_interval=retry_interval if retry_interval is not None else self.default_config.retry_interval,
            )
            
            # 确定超时时间（转为毫秒）
            timeout_ms = int((cfg.timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
            
            # 构建操作描述
            name = action_name or func.__name__
            desc = description or (args[0] if args else name)
            
            # 构建参数信息（用于日志）
            param_info = []
            if cfg.timeout:
                param_info.append(f"timeout={cfg.timeout}s")
            if cfg.wait_before > 0:
                param_info.append(f"wait_before={cfg.wait_before}s")
            if cfg.wait_after > 0:
                param_info.append(f"wait_after={cfg.wait_after}s")
            if cfg.error_strategy != ErrorStrategy.RAISE:
                param_info.append(f"strategy={cfg.error_strategy.value}")
            if cfg.retry_times > 0:
                param_info.append(f"retry={cfg.retry_times}")
            
            params_str = f" [{', '.join(param_info)}]" if param_info else ""
            
            # 记录开始日志
            logger.REMOTE.info(f"[{name}][开始] {desc}{params_str}")
            
            # 执行前等待
            if cfg.wait_before > 0:
                await asyncio.sleep(cfg.wait_before)
            
            # 执行操作（带重试）
            attempt = 0
            # 只有 RETRY 策略才需要重试，其他策略只尝试一次
            max_attempts = (cfg.retry_times + 1) if cfg.error_strategy == ErrorStrategy.RETRY else 1
            last_exception = None
            start_time = time.time()
            
            while attempt < max_attempts:
                try:
                    # 如果函数需要timeout参数，传入timeout
                    if 'timeout' in func.__code__.co_varnames:
                        result = await func(self, *args, timeout=timeout_ms, **kwargs)
                    else:
                        result = await func(self, *args, **kwargs)
                    
                    # 执行后等待
                    if cfg.wait_after > 0:
                        await asyncio.sleep(cfg.wait_after)
                    
                    # 计算耗时
                    elapsed = time.time() - start_time
                    elapsed_str = f"{elapsed:.2f}s"
                    
                    # 成功日志
                    if attempt > 0:
                        logger.REMOTE.info(f"[{name}][成功] 重试成功 (第{attempt + 1}次尝试, 耗时{elapsed_str}) - {desc}")
                    else:
                        logger.REMOTE.info(f"[{name}][成功] 操作完成 (耗时{elapsed_str}) - {desc}")
                    
                    return result
                    
                except Exception as e:
                    last_exception = e
                    attempt += 1
                    elapsed = time.time() - start_time
                    
                    # 记录异常信息
                    error_type = type(e).__name__
                    error_msg = str(e)
                    # 截断过长的错误信息
                    if len(error_msg) > 200:
                        error_msg = error_msg[:200] + "..."
                    
                    logger.REMOTE.warning(
                        f"[{name}][失败] 第{attempt}/{max_attempts}次尝试失败 "
                        f"(耗时{elapsed:.2f}s) - {desc}\n"
                        f"  └─ 异常类型: {error_type}\n"
                        f"  └─ 异常信息: {error_msg}"
                    )
                    
                    # 判断是否继续重试
                    if attempt < max_attempts and cfg.error_strategy == ErrorStrategy.RETRY:
                        logger.REMOTE.info(
                            f"[{name}][重试] {cfg.retry_interval}s 后进行第{attempt + 1}次重试 "
                            f"(剩余{max_attempts - attempt}次机会)"
                        )
                        await asyncio.sleep(cfg.retry_interval)
                        continue
                    else:
                        break
            
            # 所有尝试都失败后的处理
            if last_exception:
                elapsed = time.time() - start_time
                error_type = type(last_exception).__name__
                error_msg = str(last_exception)
                
                if cfg.error_strategy == ErrorStrategy.IGNORE:
                    logger.REMOTE.warning(
                        f"[{name}][忽略] 操作失败但已忽略 (耗时{elapsed:.2f}s) - {desc}\n"
                        f"  └─ 异常类型: {error_type}\n"
                        f"  └─ 异常信息: {error_msg}"
                    )
                    return None
                elif cfg.error_strategy == ErrorStrategy.RAISE:
                    logger.REMOTE.error(
                        f"[{name}][异常] 操作失败，抛出异常 (耗时{elapsed:.2f}s) - {desc}\n"
                        f"  └─ 异常类型: {error_type}\n"
                        f"  └─ 异常信息: {error_msg}"
                    )
                    logger.REMOTE.debug(f"[{name}][堆栈]\n{traceback.format_exc()}")
                    raise last_exception
                else:  # RETRY 但已达到最大重试次数
                    logger.REMOTE.error(
                        f"[{name}][异常] 重试{cfg.retry_times}次后仍失败 (总耗时{elapsed:.2f}s) - {desc}\n"
                        f"  └─ 异常类型: {error_type}\n"
                        f"  └─ 异常信息: {error_msg}"
                    )
                    logger.REMOTE.debug(f"[{name}][堆栈]\n{traceback.format_exc()}")
                    raise last_exception
        
        return wrapper
    return decorator


class BasePage:
    """页面基类（异步版本）- 实现POM模式的核心，支持丰富的配置能力"""
    
    def __init__(self, page: Page, browser_manager=None, **default_config):
        """
        初始化页面对象
        
        Args:
            page: Playwright Page对象
            browser_manager: BrowserManager实例（可选，用于获取配置信息）
            **default_config: 默认配置参数
                - timeout: 超时时间（秒）
                - wait_before: 执行前等待（秒）
                - wait_after: 执行后等待（秒）
                - error_strategy: 异常策略 (ErrorStrategy枚举)
                - retry_times: 重试次数
                - retry_interval: 重试间隔（秒）
        """
        self.page = page
        self._browser_manager = browser_manager  # 保存 BrowserManager 引用
        
        # 默认配置
        self.default_config = ActionConfig(
            timeout=default_config.get('timeout', DEFAULT_TIMEOUT / 1000),
            wait_before=default_config.get('wait_before', 0),
            wait_after=default_config.get('wait_after', 0),
            error_strategy=default_config.get('error_strategy', ErrorStrategy.RAISE),
            retry_times=default_config.get('retry_times', 0),
            retry_interval=default_config.get('retry_interval', 1.0),
        )
    
    def update_default_config(self, **kwargs):
        """
        更新默认配置
        
        Args:
            **kwargs: 配置参数
        """
        self.default_config = self.default_config.merge(**kwargs)
    
    # ========== 导航相关 ==========
    
    @action_wrapper("导航")
    async def navigate(self, url: str = None, **kwargs) -> None:
        """
        导航到指定URL
        
        Args:
            url: 目标URL，如果为None则使用配置的base_url
            wait_until: 等待状态 ("load" | "domcontentloaded" | "networkidle" | "commit")
            **kwargs: 通用配置参数
        """
        target_url = url
        wait_until = kwargs.pop('wait_until', 'load')
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        
        await self.page.goto(target_url, wait_until=wait_until, timeout=timeout_ms)
    
    @action_wrapper("后退")
    async def go_back(self, **kwargs) -> None:
        """后退到上一页"""
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        await self.page.go_back(timeout=timeout_ms)
    
    @action_wrapper("前进")
    async def go_forward(self, **kwargs) -> None:
        """前进到下一页"""
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        await self.page.go_forward(timeout=timeout_ms)
    
    @action_wrapper("刷新")
    async def reload(self, **kwargs) -> None:
        """刷新页面"""
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        await self.page.reload(timeout=timeout_ms)
    
    # ========== 元素交互 ==========
    
    @action_wrapper("点击")
    async def click(self, selector: str, timeout: int = None, **kwargs) -> None:
        """
        点击元素
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            button: 鼠标按钮 ("left" | "right" | "middle")
            click_count: 点击次数
            modifiers: 修饰键 ["Alt", "Control", "Meta", "Shift"]
            position: 点击位置 {"x": 0, "y": 0}
            force: 是否强制点击
            **kwargs: 通用配置参数
        """
        button = kwargs.pop('button', 'left')
        click_count = kwargs.pop('click_count', 1)
        modifiers = kwargs.pop('modifiers', None)
        position = kwargs.pop('position', None)
        force = kwargs.pop('force', False)
        
        await self.page.click(
            selector,
            timeout=timeout,
            button=button,
            click_count=click_count,
            modifiers=modifiers,
            position=position,
            force=force
        )
    
    @action_wrapper("双击")
    async def dblclick(self, selector: str, timeout: int = None, **kwargs) -> None:
        """
        双击元素
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
        """
        await self.page.dblclick(selector, timeout=timeout)
    
    @action_wrapper("输入文本")
    async def fill(self, selector: str, text: str, timeout: int = None, **kwargs) -> None:
        """
        填充文本（清空后输入）
        
        Args:
            selector: 元素选择器
            text: 要填充的文本
            timeout: 超时时间（毫秒）
            force: 是否强制填充
            **kwargs: 通用配置参数
        """
        force = kwargs.pop('force', False)
        await self.page.fill(selector, text, timeout=timeout, force=force)
    
    @action_wrapper("模拟键盘输入")
    async def type(self, selector: str, text: str, timeout: int = None, **kwargs) -> None:
        """
        模拟键盘输入（逐字符输入）
        
        Args:
            selector: 元素选择器
            text: 要输入的文本
            timeout: 超时时间（毫秒）
            delay: 每个字符之间的延迟（毫秒）
            **kwargs: 通用配置参数
        """
        delay = kwargs.pop('delay', 100)
        await self.page.type(selector, text, delay=delay, timeout=timeout)
    
    @action_wrapper("按键")
    async def press(self, selector: str, key: str, timeout: int = None, **kwargs) -> None:
        """
        按键
        
        Args:
            selector: 元素选择器
            key: 按键名称（如 'Enter', 'Escape', 'Control+A'）
            timeout: 超时时间（毫秒）
            delay: 按键延迟（毫秒）
            **kwargs: 通用配置参数
        """
        delay = kwargs.pop('delay', 0)
        await self.page.press(selector, key, delay=delay, timeout=timeout)
    
    @action_wrapper("选择下拉选项")
    async def select_option(
        self,
        selector: str,
        value: Union[str, List[str]] = None,
        timeout: int = None,
        **kwargs
    ) -> List[str]:
        """
        选择下拉选项
        
        Args:
            selector: 元素选择器
            value: 选项值（字符串或列表）
            timeout: 超时时间（毫秒）
            label: 按标签选择
            index: 按索引选择
            **kwargs: 通用配置参数
            
        Returns:
            选中的选项值列表
        """
        label = kwargs.pop('label', None)
        index = kwargs.pop('index', None)
        
        if value is not None:
            return await self.page.select_option(selector, value=value, timeout=timeout)
        elif label is not None:
            return await self.page.select_option(selector, label=label, timeout=timeout)
        elif index is not None:
            return await self.page.select_option(selector, index=index, timeout=timeout)
        else:
            raise ValueError("必须提供 value、label 或 index 参数")
    
    @action_wrapper("勾选")
    async def check(self, selector: str, timeout: int = None, **kwargs) -> None:
        """
        勾选复选框或单选框
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            force: 是否强制勾选
            **kwargs: 通用配置参数
        """
        force = kwargs.pop('force', False)
        await self.page.check(selector, timeout=timeout, force=force)
    
    @action_wrapper("取消勾选")
    async def uncheck(self, selector: str, timeout: int = None, **kwargs) -> None:
        """
        取消勾选复选框
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            force: 是否强制取消勾选
            **kwargs: 通用配置参数
        """
        force = kwargs.pop('force', False)
        await self.page.uncheck(selector, timeout=timeout, force=force)
    
    @action_wrapper("上传文件")
    async def set_input_files(
        self,
        selector: str,
        files: Union[str, List[str]],
        timeout: int = None,
        **kwargs
    ) -> None:
        """
        上传文件
        
        Args:
            selector: 文件输入框选择器
            files: 文件路径（字符串或列表）
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
        """
        await self.page.set_input_files(selector, files, timeout=timeout)
    
    @action_wrapper("悬停")
    async def hover(self, selector: str, timeout: int = None, **kwargs) -> None:
        """
        鼠标悬停
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            position: 悬停位置 {"x": 0, "y": 0}
            force: 是否强制悬停
            **kwargs: 通用配置参数
        """
        position = kwargs.pop('position', None)
        force = kwargs.pop('force', False)
        await self.page.hover(selector, timeout=timeout, position=position, force=force)
    
    @action_wrapper("拖拽")
    async def drag_and_drop(
        self,
        source: str,
        target: str,
        timeout: int = None,
        **kwargs
    ) -> None:
        """
        拖拽元素
        
        Args:
            source: 源元素选择器
            target: 目标元素选择器
            timeout: 超时时间（毫秒）
            force: 是否强制拖拽
            **kwargs: 通用配置参数
        """
        force = kwargs.pop('force', False)
        await self.page.drag_and_drop(source, target, timeout=timeout, force=force)
    
    @action_wrapper("聚焦")
    async def focus(self, selector: str, timeout: int = None, **kwargs) -> None:
        """
        聚焦元素

        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
        """
        await self.page.focus(selector, timeout=timeout)

    @action_wrapper("分派事件")
    async def dispatch_event(self, selector: str, type: str, event_init: dict = None, **kwargs) -> None:
        """
        分派DOM事件到元素

        Args:
            selector: 元素选择器
            type: 事件类型
            event_init: 事件初始化参数
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        await self.page.dispatch_event(selector, type, event_init, timeout=timeout_ms)

    @action_wrapper("设置选中状态")
    async def set_checked(self, selector: str, checked: bool, **kwargs) -> None:
        """
        设置复选框或单选框的选中状态

        Args:
            selector: 元素选择器
            checked: 是否选中
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        await self.page.set_checked(selector, checked, timeout=timeout_ms)
    
    @action_wrapper("滚动到元素")
    async def scroll_to(self, selector: str, timeout: int = None, **kwargs) -> None:
        """
        滚动到元素
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
        """
        await self.page.locator(selector).scroll_into_view_if_needed(timeout=timeout)
    
    # ========== 等待相关 ==========
    
    @action_wrapper("等待元素")
    async def wait_for_selector(
        self,
        selector: str,
        timeout: int = None,
        **kwargs
    ) -> Locator:
        """
        等待元素
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            state: 等待状态 ("attached" | "detached" | "visible" | "hidden")
            **kwargs: 通用配置参数
            
        Returns:
            Locator对象
        """
        state = kwargs.pop('state', 'visible')
        return await self.page.wait_for_selector(selector, state=state, timeout=timeout)
    
    @action_wrapper("等待URL")
    async def wait_for_url(self, url: str, timeout: int = None, **kwargs) -> None:
        """
        等待URL变化
        
        Args:
            url: 目标URL（支持glob模式和正则）
            timeout: 超时时间（毫秒）
            wait_until: 等待状态
            **kwargs: 通用配置参数
        """
        wait_until = kwargs.pop('wait_until', 'load')
        await self.page.wait_for_url(url, timeout=timeout, wait_until=wait_until)
    
    @action_wrapper("等待加载状态")
    async def wait_for_load_state(self, state: str = "load", timeout: int = None, **kwargs) -> None:
        """
        等待加载状态
        
        Args:
            state: 加载状态 ("load" | "domcontentloaded" | "networkidle")
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
        """
        await self.page.wait_for_load_state(state, timeout=timeout)
    
    @action_wrapper("等待")
    async def wait(self, seconds: float, **kwargs) -> None:
        """
        等待指定时间

        Args:
            seconds: 等待时间（秒）
            **kwargs: 通用配置参数（此方法会忽略timeout参数）
        """
        await asyncio.sleep(seconds)

    @action_wrapper("等待事件")
    async def wait_for_event(self, event: str, **kwargs) -> Any:
        """
        等待指定事件发生

        Args:
            event: 事件名称
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件数据
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return await self.page.wait_for_event(event, timeout=timeout_ms)

    @action_wrapper("等待时间")
    async def wait_for_timeout(self, seconds: float):
        """
        等待指定时间

        Args:
            seconds: 等待时间（秒）

        """
        milliseconds = float(seconds * 1000)
        await self.page.wait_for_timeout(milliseconds)

    @action_wrapper("等待函数")
    async def wait_for_function(self, expression: str, arg: Any = None, **kwargs) -> Any:
        """
        等待JavaScript函数返回真值

        Args:
            expression: JavaScript表达式
            arg: 参数
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            JSHandle对象
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return await self.page.wait_for_function(expression, arg=arg, timeout=timeout_ms)
    
    # ========== 获取信息 ==========
    
    @action_wrapper("获取文本")
    async def get_text(self, selector: str, timeout: int = None, **kwargs) -> str:
        """
        获取元素文本
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
            
        Returns:
            元素文本内容
        """
        text = await self.page.text_content(selector, timeout=timeout)
        if text:
            # 截断过长的文本
            display_text = text if len(text) <= 100 else text[:100] + "..."
            logger.REMOTE.debug(f"  └─ 文本内容: {display_text}")
        return text or ""
    
    @action_wrapper("获取内部文本")
    async def get_inner_text(self, selector: str, timeout: int = None, **kwargs) -> str:
        """
        获取元素内部文本（渲染后的文本）
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
            
        Returns:
            元素内部文本
        """
        text = await self.page.inner_text(selector, timeout=timeout)
        if text:
            display_text = text if len(text) <= 100 else text[:100] + "..."
            logger.REMOTE.debug(f"  └─ 内部文本: {display_text}")
        return text
    
    @action_wrapper("获取HTML")
    async def get_inner_html(self, selector: str, timeout: int = None, **kwargs) -> str:
        """
        获取元素HTML
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
            
        Returns:
            元素HTML内容
        """
        html = await self.page.inner_html(selector, timeout=timeout)
        return html
    
    @action_wrapper("获取属性")
    async def get_attribute(self, selector: str, attribute: str, timeout: int = None, **kwargs) -> Optional[str]:
        """
        获取元素属性
        
        Args:
            selector: 元素选择器
            attribute: 属性名
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
            
        Returns:
            属性值
        """
        value = await self.page.get_attribute(selector, attribute, timeout=timeout)
        if value:
            display_value = value if len(value) <= 100 else value[:100] + "..."
            logger.REMOTE.debug(f"  └─ 属性[{attribute}]: {display_value}")
        return value
    
    @action_wrapper("获取输入值")
    async def get_input_value(self, selector: str, timeout: int = None, **kwargs) -> str:
        """
        获取输入框的值
        
        Args:
            selector: 元素选择器
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
            
        Returns:
            输入框的值
        """
        value = await self.page.input_value(selector, timeout=timeout)
        if value:
            display_value = value if len(value) <= 100 else value[:100] + "..."
            logger.REMOTE.debug(f"  └─ 输入值: {display_value}")
        return value
    
    # ========== 状态检查 ==========
    
    async def is_visible(self, selector: str, **kwargs) -> bool:
        """检查元素是否可见"""
        visible = await self.page.is_visible(selector)
        logger.REMOTE.debug(f"  └─ 元素可见性: {visible}")
        return visible
    
    async def is_hidden(self, selector: str, **kwargs) -> bool:
        """检查元素是否隐藏"""
        hidden = await self.page.is_hidden(selector)
        logger.REMOTE.debug(f"  └─ 元素隐藏性: {hidden}")
        return hidden
    
    async def is_enabled(self, selector: str, **kwargs) -> bool:
        """检查元素是否可用"""
        enabled = await self.page.is_enabled(selector)
        logger.REMOTE.debug(f"  └─ 元素可用性: {enabled}")
        return enabled
    
    async def is_disabled(self, selector: str, **kwargs) -> bool:
        """检查元素是否禁用"""
        disabled = await self.page.is_disabled(selector)
        logger.REMOTE.debug(f"  └─ 元素禁用性: {disabled}")
        return disabled
    
    async def is_checked(self, selector: str, **kwargs) -> bool:
        """检查复选框/单选框是否选中"""
        checked = await self.page.is_checked(selector)
        logger.REMOTE.debug(f"  └─ 元素选中状态: {checked}")
        return checked
    
    async def is_editable(self, selector: str, **kwargs) -> bool:
        """检查元素是否可编辑"""
        editable = await self.page.is_editable(selector)
        logger.REMOTE.debug(f"  └─ 元素可编辑性: {editable}")
        return editable
    
    @action_wrapper("获取元素数量")
    async def count(self, selector: str, **kwargs) -> int:
        """
        获取匹配元素的数量
        
        Args:
            selector: 元素选择器
            **kwargs: 通用配置参数
            
        Returns:
            元素数量
        """
        count = await self.page.locator(selector).count()
        logger.REMOTE.debug(f"  └─ 元素数量: {count}")
        return count
    
    # ========== 截图相关 ==========
    
    @action_wrapper("截图")
    async def screenshot(self, path: str = None, **kwargs) -> Optional[bytes]:
        """
        页面截图
        
        Args:
            path: 保存路径
            full_page: 是否全页截图
            type: 图片类型 ("png" | "jpeg")
            quality: 图片质量 (0-100, 仅jpeg)
            clip: 截图区域 {"x": 0, "y": 0, "width": 100, "height": 100}
            **kwargs: 通用配置参数
            
        Returns:
            如果没有指定path，返回图片字节
        """
        full_page = kwargs.pop('full_page', False)
        img_type = kwargs.pop('type', 'png')
        quality = kwargs.pop('quality', None)
        clip = kwargs.pop('clip', None)
        
        if path:
            logger.REMOTE.debug(f"  └─ 保存路径: {path}")
            logger.REMOTE.debug(f"  └─ 截图配置: full_page={full_page}, type={img_type}")
            await self.page.screenshot(
                path=path,
                full_page=full_page,
                type=img_type,
                quality=quality,
                clip=clip
            )
            return None
        else:
            logger.REMOTE.debug(f"  └─ 返回字节流")
            return await self.page.screenshot(
                full_page=full_page,
                type=img_type,
                quality=quality,
                clip=clip
            )
    
    @action_wrapper("元素截图")
    async def screenshot_element(self, selector: str, path: str = None, timeout: int = None, **kwargs) -> Optional[bytes]:
        """
        元素截图
        
        Args:
            selector: 元素选择器
            path: 保存路径
            timeout: 超时时间（毫秒）
            **kwargs: 通用配置参数
            
        Returns:
            如果没有指定path，返回图片字节
        """
        locator = self.page.locator(selector)
        
        if path:
            logger.REMOTE.debug(f"  └─ 保存路径: {path}")
            await locator.screenshot(path=path, timeout=timeout)
            return None
        else:
            logger.REMOTE.debug(f"  └─ 返回字节流")
            return await locator.screenshot(timeout=timeout)
    
    # ========== JavaScript执行 ==========
    
    @action_wrapper("执行JavaScript")
    async def evaluate(self, expression: str, arg: Any = None, **kwargs) -> Any:
        """
        执行JavaScript代码

        Args:
            expression: JavaScript表达式
            arg: 传递给JavaScript的参数
            **kwargs: 通用配置参数

        Returns:
            JavaScript执行结果
        """
        result = await self.page.evaluate(expression, arg)
        # 截断过长的结果
        result_str = str(result)
        if len(result_str) > 200:
            result_str = result_str[:200] + "..."
        logger.REMOTE.debug(f"  └─ 执行结果: {result_str}")
        return result

    @action_wrapper("执行JavaScript并返回句柄")
    async def evaluate_handle(self, expression: str, arg: Any = None, **kwargs) -> Any:
        """
        执行JavaScript代码并返回句柄

        Args:
            expression: JavaScript表达式
            arg: 传递给JavaScript的参数
            **kwargs: 通用配置参数

        Returns:
            JSHandle对象
        """
        return await self.page.evaluate_handle(expression, arg)

    @action_wrapper("在元素上执行JavaScript")
    async def eval_on_selector(self, selector: str, expression: str, arg: Any = None, **kwargs) -> Any:
        """
        在指定元素上执行JavaScript

        Args:
            selector: 元素选择器
            expression: JavaScript表达式
            arg: 传递给JavaScript的参数
            **kwargs: 通用配置参数

        Returns:
            JavaScript执行结果
        """
        result = await self.page.eval_on_selector(selector, expression, arg)
        # 截断过长的结果
        result_str = str(result)
        if len(result_str) > 200:
            result_str = result_str[:200] + "..."
        logger.REMOTE.debug(f"  └─ 执行结果: {result_str}")
        return result

    @action_wrapper("在所有匹配元素上执行JavaScript")
    async def eval_on_selector_all(self, selector: str, expression: str, arg: Any = None, **kwargs) -> Any:
        """
        在所有匹配元素上执行JavaScript

        Args:
            selector: 元素选择器
            expression: JavaScript表达式
            arg: 传递给JavaScript的参数
            **kwargs: 通用配置参数

        Returns:
            JavaScript执行结果
        """
        result = await self.page.eval_on_selector_all(selector, expression, arg)
        # 截断过长的结果
        result_str = str(result)
        if len(result_str) > 200:
            result_str = result_str[:200] + "..."
        logger.REMOTE.debug(f"  └─ 执行结果: {result_str}")
        return result

    @action_wrapper("添加初始化脚本")
    async def add_init_script(self, script: str = None, path: str = None, **kwargs) -> None:
        """
        添加页面初始化脚本

        Args:
            script: JavaScript代码
            path: 脚本文件路径
            **kwargs: 通用配置参数
        """
        await self.page.add_init_script(script=script, path=path)

    @action_wrapper("暴露函数到页面")
    async def expose_function(self, name: str, callback: Callable, **kwargs) -> None:
        """
        将Python函数暴露给页面JavaScript

        Args:
            name: 函数名
            callback: Python函数
            **kwargs: 通用配置参数
        """
        await self.page.expose_function(name, callback)

    @action_wrapper("暴露绑定到页面")
    async def expose_binding(self, name: str, callback: Callable, **kwargs) -> None:
        """
        将Python函数绑定暴露给页面JavaScript

        Args:
            name: 绑定名
            callback: Python函数
            **kwargs: 通用配置参数
        """
        await self.page.expose_binding(name, callback)

    @action_wrapper("添加脚本标签")
    async def add_script_tag(self, url: str = None, path: str = None, content: str = None, **kwargs) -> Any:
        """
        添加<script>标签到页面

        Args:
            url: 脚本URL
            path: 脚本文件路径
            content: 脚本内容
            **kwargs: 通用配置参数

        Returns:
            添加的脚本元素
        """
        return await self.page.add_script_tag(url=url, path=path, content=content)

    @action_wrapper("添加样式标签")
    async def add_style_tag(self, url: str = None, path: str = None, content: str = None, **kwargs) -> Any:
        """
        添加<link>或<style>标签到页面

        Args:
            url: 样式URL
            path: 样式文件路径
            content: 样式内容
            **kwargs: 通用配置参数

        Returns:
            添加的样式元素
        """
        return await self.page.add_style_tag(url=url, path=path, content=content)
    
    # ========== Locator相关 ==========
    
    # ========== 元素查询 ==========

    @action_wrapper("查询元素")
    async def query_selector(self, selector: str, **kwargs) -> Any:
        """
        查询单个元素

        Args:
            selector: 元素选择器
            **kwargs: 通用配置参数

        Returns:
            ElementHandle对象或None
        """
        return await self.page.query_selector(selector)

    @action_wrapper("查询所有元素")
    async def query_selector_all(self, selector: str, **kwargs) -> List[Any]:
        """
        查询所有匹配的元素

        Args:
            selector: 元素选择器
            **kwargs: 通用配置参数

        Returns:
            ElementHandle对象列表
        """
        return await self.page.query_selector_all(selector)

    # ========== 高级定位器 ==========

    def get_by_text(self, text: Union[str, typing.Pattern[str]], **kwargs) -> Locator:
        """
        通过文本内容定位元素

        Args:
            text: 文本内容
            exact: 是否精确匹配
            **kwargs: 其他参数

        Returns:
            Locator对象
        """
        exact = kwargs.pop('exact', False)
        return self.page.get_by_text(text, exact=exact)

    def get_by_alt_text(self, text: Union[str, typing.Pattern[str]], **kwargs) -> Locator:
        """
        通过alt属性定位元素

        Args:
            text: alt文本
            exact: 是否精确匹配
            **kwargs: 其他参数

        Returns:
            Locator对象
        """
        exact = kwargs.pop('exact', False)
        return self.page.get_by_alt_text(text, exact=exact)

    def get_by_label(self, text: Union[str, typing.Pattern[str]], **kwargs) -> Locator:
        """
        通过label定位元素

        Args:
            text: label文本
            exact: 是否精确匹配
            **kwargs: 其他参数

        Returns:
            Locator对象
        """
        exact = kwargs.pop('exact', False)
        return self.page.get_by_label(text, exact=exact)

    def get_by_placeholder(self, text: Union[str, typing.Pattern[str]], **kwargs) -> Locator:
        """
        通过placeholder定位元素

        Args:
            text: placeholder文本
            exact: 是否精确匹配
            **kwargs: 其他参数

        Returns:
            Locator对象
        """
        exact = kwargs.pop('exact', False)
        return self.page.get_by_placeholder(text, exact=exact)

    def get_by_role(self, role: str, **kwargs) -> Locator:
        """
        通过ARIA role定位元素

        Args:
            role: ARIA role
            **kwargs: 其他参数

        Returns:
            Locator对象
        """
        return self.page.get_by_role(role, **kwargs)

    def get_by_test_id(self, test_id: Union[str, typing.Pattern[str]]) -> Locator:
        """
        通过test id定位元素

        Args:
            test_id: 测试ID

        Returns:
            Locator对象
        """
        return self.page.get_by_test_id(test_id)

    def get_by_title(self, text: Union[str, typing.Pattern[str]], **kwargs) -> Locator:
        """
        通过title属性定位元素

        Args:
            text: title文本
            exact: 是否精确匹配
            **kwargs: 其他参数

        Returns:
            Locator对象
        """
        exact = kwargs.pop('exact', False)
        return self.page.get_by_title(text, exact=exact)

    def frame_locator(self, selector: str) -> Any:
        """
        获取frame定位器

        Args:
            selector: frame选择器

        Returns:
            FrameLocator对象
        """
        return self.page.frame_locator(selector)

    def frame(self, name: str = None, **kwargs) -> Any:
        """
        获取frame

        Args:
            name: frame名称
            url: frame URL
            **kwargs: 其他参数

        Returns:
            Frame对象
        """
        url = kwargs.pop('url', None)
        return self.page.frame(name=name, url=url)

    def locator(self, selector: str) -> Locator:
        """
        获取Locator对象（用于链式调用）

        Args:
            selector: 元素选择器

        Returns:
            Locator对象
        """
        return self.page.locator(selector)
    
    # ========== 属性访问 ==========
    
    # ========== 属性访问 ==========

    @property
    def url(self) -> str:
        """获取当前URL"""
        return self.page.url

    @property
    def context(self) -> Any:
        """获取浏览器上下文"""
        return self.page.context

    @property
    def main_frame(self) -> Any:
        """获取主frame"""
        return self.page.main_frame

    @property
    def frames(self) -> List[Any]:
        """获取所有frames"""
        return self.page.frames

    @property
    def workers(self) -> List[Any]:
        """获取所有Web Workers"""
        return self.page.workers

    @property
    def keyboard(self) -> Any:
        """获取键盘对象"""
        return self.page.keyboard

    @property
    def mouse(self) -> Any:
        """获取鼠标对象"""
        return self.page.mouse

    @property
    def touchscreen(self) -> Any:
        """获取触摸屏对象"""
        return self.page.touchscreen

    @property
    def accessibility(self) -> Any:
        """获取无障碍对象"""
        return self.page.accessibility

    @property
    def request(self) -> Any:
        """获取API请求上下文"""
        return self.page.request

    @property
    def video(self) -> Any:
        """获取视频录制对象"""
        return self.page.video

    @property
    def viewport_size(self) -> Any:
        """获取视口大小"""
        return self.page.viewport_size

    async def get_title(self) -> str:
        """获取页面标题"""
        return await self.page.title()

    async def opener(self) -> Any:
        """获取打开此页面的页面"""
        return await self.page.opener()

    def is_closed(self) -> bool:
        """检查页面是否已关闭"""
        return self.page.is_closed()

    async def request_gc(self) -> None:
        """请求垃圾回收"""
        await self.page.request_gc()

    # ========== 路由和网络 ==========

    @action_wrapper("路由请求")
    async def route(self, url: Union[str, typing.Pattern[str], Callable], handler: Callable, **kwargs) -> None:
        """
        设置请求路由

        Args:
            url: URL模式
            handler: 处理函数
            times: 处理次数
            **kwargs: 通用配置参数
        """
        times = kwargs.pop('times', None)
        await self.page.route(url, handler, times=times)

    @action_wrapper("取消路由")
    async def unroute(self, url: Union[str, typing.Pattern[str], Callable] = None, handler: Callable = None, **kwargs) -> None:
        """
        移除请求路由

        Args:
            url: URL模式
            handler: 处理函数
            **kwargs: 通用配置参数
        """
        await self.page.unroute(url, handler)

    @action_wrapper("取消所有路由")
    async def unroute_all(self, **kwargs) -> None:
        """
        移除所有请求路由

        Args:
            behavior: 行为 ("default" | "ignoreErrors" | "wait")
            **kwargs: 通用配置参数
        """
        behavior = kwargs.pop('behavior', None)
        await self.page.unroute_all(behavior=behavior)

    @action_wrapper("路由WebSocket")
    async def route_web_socket(self, url: Union[str, typing.Pattern[str], Callable], handler: Callable, **kwargs) -> None:
        """
        设置WebSocket路由

        Args:
            url: URL模式
            handler: 处理函数
            **kwargs: 通用配置参数
        """
        await self.page.route_web_socket(url, handler)

    @action_wrapper("从HAR路由")
    async def route_from_har(self, har: str, **kwargs) -> None:
        """
        从HAR文件设置路由

        Args:
            har: HAR文件路径
            url: URL模式
            not_found: 未找到时的行为
            update: 是否更新HAR
            **kwargs: 通用配置参数
        """
        url = kwargs.pop('url', None)
        not_found = kwargs.pop('not_found', None)
        update = kwargs.pop('update', None)
        update_content = kwargs.pop('update_content', None)
        update_mode = kwargs.pop('update_mode', None)

        await self.page.route_from_har(
            har=har,
            url=url,
            not_found=not_found,
            update=update,
            update_content=update_content,
            update_mode=update_mode
        )

    # ========== 事件处理 ==========

    def on(self, event: str, handler: Callable) -> None:
        """
        添加事件监听器

        Args:
            event: 事件名
            handler: 处理函数
        """
        self.page.on(event, handler)

    def once(self, event: str, handler: Callable) -> None:
        """
        添加一次性事件监听器

        Args:
            event: 事件名
            handler: 处理函数
        """
        self.page.once(event, handler)

    # ========== 期望事件 ==========

    def expect_event(self, event: str, **kwargs) -> Any:
        """
        期望事件发生

        Args:
            event: 事件名
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_event(event, timeout=timeout_ms)

    def expect_console_message(self, **kwargs) -> Any:
        """
        期望控制台消息

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_console_message(timeout=timeout_ms)

    def expect_download(self, **kwargs) -> Any:
        """
        期望下载

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_download(timeout=timeout_ms)

    def expect_file_chooser(self, **kwargs) -> Any:
        """
        期望文件选择器

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_file_chooser(timeout=timeout_ms)

    def expect_navigation(self, **kwargs) -> Any:
        """
        期望导航

        Args:
            url: 目标URL
            wait_until: 等待状态
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        url = kwargs.pop('url', None)
        wait_until = kwargs.pop('wait_until', None)
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_navigation(url=url, wait_until=wait_until, timeout=timeout_ms)

    def expect_popup(self, **kwargs) -> Any:
        """
        期望弹出窗口

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_popup(timeout=timeout_ms)

    def expect_request(self, url_or_predicate: Union[str, typing.Pattern[str], Callable], **kwargs) -> Any:
        """
        期望请求

        Args:
            url_or_predicate: URL或谓词
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_request(url_or_predicate, timeout=timeout_ms)

    def expect_request_finished(self, **kwargs) -> Any:
        """
        期望请求完成

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_request_finished(timeout=timeout_ms)

    def expect_response(self, url_or_predicate: Union[str, typing.Pattern[str], Callable], **kwargs) -> Any:
        """
        期望响应

        Args:
            url_or_predicate: URL或谓词
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_response(url_or_predicate, timeout=timeout_ms)

    def expect_websocket(self, **kwargs) -> Any:
        """
        期望WebSocket

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_websocket(timeout=timeout_ms)

    def expect_worker(self, **kwargs) -> Any:
        """
        期望Worker

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数

        Returns:
            事件上下文管理器
        """
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        return self.page.expect_worker(timeout=timeout_ms)

    # ========== 定位器处理 ==========

    @action_wrapper("添加定位器处理器")
    async def add_locator_handler(self, locator: Locator, handler: Callable, **kwargs) -> None:
        """
        添加定位器处理器

        Args:
            locator: 定位器
            handler: 处理函数
            no_wait_after: 处理后不等待
            times: 处理次数
            **kwargs: 通用配置参数
        """
        no_wait_after = kwargs.pop('no_wait_after', None)
        times = kwargs.pop('times', None)
        await self.page.add_locator_handler(locator, handler, no_wait_after=no_wait_after, times=times)

    @action_wrapper("移除定位器处理器")
    async def remove_locator_handler(self, locator: Locator, **kwargs) -> None:
        """
        移除定位器处理器

        Args:
            locator: 定位器
            **kwargs: 通用配置参数
        """
        await self.page.remove_locator_handler(locator)

    # ========== 页面基本操作 ==========

    @action_wrapper("关闭页面")
    async def close(self, **kwargs) -> None:
        """
        关闭页面

        Args:
            run_before_unload: 是否运行卸载前处理
            reason: 关闭原因
            **kwargs: 通用配置参数
        """
        run_before_unload = kwargs.pop('run_before_unload', False)
        reason = kwargs.pop('reason', None)
        await self.page.close(run_before_unload=run_before_unload, reason=reason)

    @action_wrapper("获取页面HTML")
    async def get_content(self, **kwargs) -> str:
        """
        获取页面完整HTML内容

        Returns:
            页面HTML内容
        """
        content = await self.page.content()
        return content

    @action_wrapper("设置页面内容")
    async def set_content(self, html: str, **kwargs) -> None:
        """
        设置页面HTML内容

        Args:
            html: HTML内容
            wait_until: 等待状态 ("load" | "domcontentloaded" | "networkidle")
            **kwargs: 通用配置参数
        """
        wait_until = kwargs.pop('wait_until', 'load')
        timeout = kwargs.get('timeout', self.default_config.timeout)
        timeout_ms = int((timeout or (DEFAULT_TIMEOUT / 1000)) * 1000)
        await self.page.set_content(html, wait_until=wait_until, timeout=timeout_ms)

    @action_wrapper("页面置前")
    async def bring_to_front(self, **kwargs) -> None:
        """
        将页面置于前端（激活标签页）
        """
        await self.page.bring_to_front()

    @action_wrapper("暂停执行")
    async def pause(self, **kwargs) -> None:
        """
        暂停脚本执行，用于调试
        """
        await self.page.pause()

    @action_wrapper("生成PDF")
    async def pdf(self, path: str = None, **kwargs) -> Optional[bytes]:
        """
        生成页面PDF

        Args:
            path: 保存路径
            scale: 缩放比例
            display_header_footer: 是否显示页眉页脚
            header_template: 页眉模板
            footer_template: 页脚模板
            print_background: 是否打印背景
            landscape: 是否横向
            page_ranges: 页面范围
            format: 纸张格式
            width: 宽度
            height: 高度
            margin: 边距
            **kwargs: 通用配置参数

        Returns:
            如果没有指定path，返回PDF字节
        """
        scale = kwargs.pop('scale', None)
        display_header_footer = kwargs.pop('display_header_footer', None)
        header_template = kwargs.pop('header_template', None)
        footer_template = kwargs.pop('footer_template', None)
        print_background = kwargs.pop('print_background', None)
        landscape = kwargs.pop('landscape', None)
        page_ranges = kwargs.pop('page_ranges', None)
        format_size = kwargs.pop('format', None)
        width = kwargs.pop('width', None)
        height = kwargs.pop('height', None)
        margin = kwargs.pop('margin', None)

        if path:
            logger.REMOTE.debug(f"  └─ 保存路径: {path}")
            await self.page.pdf(
                path=path,
                scale=scale,
                display_header_footer=display_header_footer,
                header_template=header_template,
                footer_template=footer_template,
                print_background=print_background,
                landscape=landscape,
                page_ranges=page_ranges,
                format=format_size,
                width=width,
                height=height,
                margin=margin
            )
            return None
        else:
            logger.REMOTE.debug("  └─ 返回字节流")
            return await self.page.pdf(
                scale=scale,
                display_header_footer=display_header_footer,
                header_template=header_template,
                footer_template=footer_template,
                print_background=print_background,
                landscape=landscape,
                page_ranges=page_ranges,
                format=format_size,
                width=width,
                height=height,
                margin=margin
            )

    # ========== 页面设置 ==========

    @action_wrapper("设置视口大小")
    async def set_viewport_size(self, width: int, height: int, **kwargs) -> None:
        """
        设置页面视口大小

        Args:
            width: 宽度
            height: 高度
            **kwargs: 通用配置参数
        """
        await self.page.set_viewport_size({"width": width, "height": height})

    @action_wrapper("模拟媒体")
    async def emulate_media(
        self,
        media: Optional[str] = None,
        color_scheme: Optional[str] = None,
        **kwargs
    ) -> None:
        """
        模拟媒体类型和颜色方案

        Args:
            media: 媒体类型 ("screen" | "print" | None)
            color_scheme: 颜色方案 ("light" | "dark" | "no-preference" | None)
            **kwargs: 通用配置参数
        """
        await self.page.emulate_media(media=media, color_scheme=color_scheme)

    @action_wrapper("设置默认超时")
    def set_default_timeout(self, timeout: float, **kwargs) -> None:
        """
        设置默认超时时间（秒）

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数
        """
        timeout_ms = int(timeout * 1000)
        self.page.set_default_timeout(timeout_ms)

    @action_wrapper("设置默认导航超时")
    def set_default_navigation_timeout(self, timeout: float, **kwargs) -> None:
        """
        设置默认导航超时时间（秒）

        Args:
            timeout: 超时时间（秒）
            **kwargs: 通用配置参数
        """
        timeout_ms = int(timeout * 1000)
        self.page.set_default_navigation_timeout(timeout_ms)

    @action_wrapper("设置额外HTTP头")
    async def set_extra_http_headers(self, headers: dict, **kwargs) -> None:
        """
        设置额外的HTTP请求头

        Args:
            headers: 请求头字典
            **kwargs: 通用配置参数
        """
        await self.page.set_extra_http_headers(headers)
