"""关键字分发器"""

from typing import Any, Dict, Optional, Callable, List, Tuple
from playwright.sync_api import Page

from .page_action import KeyWordTest
from ..reference import GSTORE, MODULEDATA, INFO


class KeyRetrieval:
    """关键字分发器

    负责解析 Excel 中的关键字并分发到对应的方法执行。
    支持插件扩展关键字。
    """

    def __init__(self, page: Optional[Page] = None):
        """初始化关键字分发器

        Args:
            page: Playwright Page 实例
        """
        self._page = page or GSTORE.get('page')
        self._keyword_test: Optional[KeyWordTest] = None
        self._plugin_keywords: Dict[str, Callable] = {}
        self._element_data: Dict[str, Any] = {}

        if self._page:
            self._keyword_test = KeyWordTest(self._page)
            GSTORE['keyWord'] = self._keyword_test

    def set_page(self, page: Page) -> None:
        """设置页面实例

        Args:
            page: Playwright Page 实例
        """
        self._page = page
        self._keyword_test = KeyWordTest(page)
        GSTORE['keyWord'] = self._keyword_test

    def register_plugin_keyword(self, name: str, handler: Callable) -> None:
        """注册插件关键字

        Args:
            name: 关键字名称
            handler: 处理函数
        """
        self._plugin_keywords[name] = handler
        INFO(f"注册插件关键字: {name}")

    def register_plugin_keywords(self, keywords: Dict[str, Callable]) -> None:
        """批量注册插件关键字

        Args:
            keywords: 关键字字典 {name: handler}
        """
        self._plugin_keywords.update(keywords)
        INFO(f"注册插件关键字: {list(keywords.keys())}")

    def set_element_data(self, data: Dict[str, Any]) -> None:
        """设置元素数据（来自 elementData.yaml）

        Args:
            data: 元素数据字典
        """
        self._element_data = data

    def resolve_element(self, element_path: str) -> Tuple[str, str, Optional[int]]:
        """解析元素路径

        支持格式：
        - "module/element" - 从 elementData 获取
        - "module/element[0]" - 带索引
        - "xpath://div" - 直接定位表达式
        - "css:.class" - 直接定位表达式

        Args:
            element_path: 元素路径

        Returns:
            Tuple[targeting, element, index]: 定位类型、定位表达式、索引
        """
        index = None

        # 检查是否有索引
        if '[' in element_path and element_path.endswith(']'):
            bracket_pos = element_path.rfind('[')
            index_str = element_path[bracket_pos + 1:-1]
            element_path = element_path[:bracket_pos]
            try:
                index = int(index_str)
            except ValueError:
                if index_str == 'first':
                    index = 0
                elif index_str == 'last':
                    index = -1

        # 检查是否是直接定位表达式
        if '=' in element_path or element_path.startswith('//'):
            if element_path.startswith('xpath=') or element_path.startswith('xpath:') or element_path.startswith('//'):
                if element_path.startswith('xpath=') or element_path.startswith('xpath:'):
                    return 'xpath', element_path.split('=', 1)[1] if '=' in element_path else element_path[6:], index
                return 'xpath', element_path, index
            elif element_path.startswith('css=') or element_path.startswith('css:'):
                return 'css', element_path.split('=', 1)[1] if '=' in element_path else element_path[4:], index
            elif element_path.startswith('id='):
                return 'id', element_path[3:], index
            elif element_path.startswith('name='):
                return 'name', element_path[5:], index
            elif element_path.startswith('text='):
                return 'text', element_path[5:], index

        # 从 elementData 解析
        if '/' in element_path:
            parts = element_path.split('/')
            if len(parts) >= 2:
                module = parts[0]
                element_name = '/'.join(parts[1:])

                # 在 elementData 中查找
                if module in self._element_data:
                    module_data = self._element_data[module]
                    if element_name in module_data:
                        element_info = module_data[element_name]
                        if isinstance(element_info, list) and len(element_info) >= 2:
                            return element_info[0], element_info[1], index

        # 作为 CSS 选择器处理
        return 'css', element_path, index

    def execute(
        self,
        keyword: str,
        locator_info: List[str] = None,
        value: str = None,
        **kwargs
    ) -> Any:
        """执行关键字

        Args:
            keyword: 关键字名称
            locator_info: 定位信息列表 [element_path, ...] 或 [targeting, element, ...]
            value: 操作值
            **kwargs: 其他参数

        Returns:
            执行结果
        """
        if not self._keyword_test:
            raise RuntimeError("KeyWordTest 未初始化，请先调用 set_page()")

        # 解析定位信息
        targeting = None
        element = None
        index = None

        if locator_info:
            # 过滤空值
            locator_info = [str(item).strip() for item in locator_info if item and str(item).strip()]

            if len(locator_info) >= 1:
                # 检查是否是 elementData 路径
                first = locator_info[0]
                if '/' in first or first.startswith('//') or '=' in first:
                    targeting, element, index = self.resolve_element(first)
                    # 如果有第二个参数且是数字，作为索引
                    if len(locator_info) >= 2 and locator_info[1]:
                        try:
                            index = int(locator_info[1])
                        except ValueError:
                            pass
                else:
                    # 传统格式: [targeting, element, index?]
                    targeting = first
                    if len(locator_info) >= 2:
                        element = locator_info[1]
                    if len(locator_info) >= 3 and locator_info[2]:
                        try:
                            index = int(locator_info[2])
                        except ValueError:
                            if locator_info[2] == 'first':
                                index = 0
                            elif locator_info[2] == 'last':
                                index = -1

        # 构建参数
        params = {}
        if targeting:
            params['targeting'] = targeting
        if element:
            params['element'] = element
        if index is not None:
            params['index'] = index
        if value is not None and value != '':
            params['content'] = str(value)

        # 合并额外参数
        params.update(kwargs)

        # 查找并执行关键字
        # 1. 先检查插件关键字
        if keyword in self._plugin_keywords:
            handler = self._plugin_keywords[keyword]
            return self._call_handler(handler, params)

        # 2. 检查核心关键字
        if hasattr(self._keyword_test, keyword):
            method = getattr(self._keyword_test, keyword)
            return self._call_method(method, params)

        # 3. 关键字不存在
        raise ValueError(f"未知关键字: {keyword}")

    def _call_method(self, method: Callable, params: Dict[str, Any]) -> Any:
        """调用方法

        Args:
            method: 方法
            params: 参数字典

        Returns:
            执行结果
        """
        import inspect
        sig = inspect.signature(method)

        # 分离位置参数和关键字参数
        positional_args = []
        keyword_args = {}

        for name, param in sig.parameters.items():
            if name in ('self',):
                continue

            if param.kind == inspect.Parameter.KEYWORD_ONLY:
                # 关键字参数
                if name in params:
                    keyword_args[name] = params[name]
            elif param.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.POSITIONAL_ONLY):
                # 位置参数
                if name in params:
                    positional_args.append(params[name])
                elif param.default is not inspect.Parameter.empty:
                    # 有默认值，跳过
                    pass

        return method(*positional_args, **keyword_args)

    def _call_handler(self, handler: Callable, params: Dict[str, Any]) -> Any:
        """调用处理函数

        Args:
            handler: 处理函数
            params: 参数字典

        Returns:
            执行结果
        """
        import inspect
        sig = inspect.signature(handler)

        # 检查是否接受 **kwargs
        has_var_keyword = any(
            p.kind == inspect.Parameter.VAR_KEYWORD
            for p in sig.parameters.values()
        )

        if has_var_keyword:
            return handler(**params)

        # 过滤参数
        valid_params = {
            k: v for k, v in params.items()
            if k in sig.parameters
        }
        return handler(**valid_params)

    def get_available_keywords(self) -> List[str]:
        """获取所有可用关键字

        Returns:
            List[str]: 关键字列表
        """
        keywords = []

        # 核心关键字
        if self._keyword_test:
            for name in dir(self._keyword_test):
                if not name.startswith('_') and callable(getattr(self._keyword_test, name)):
                    keywords.append(name)

        # 插件关键字
        keywords.extend(self._plugin_keywords.keys())

        return sorted(set(keywords))

    def has_keyword(self, keyword: str) -> bool:
        """检查关键字是否存在

        Args:
            keyword: 关键字名称

        Returns:
            bool: 是否存在
        """
        if keyword in self._plugin_keywords:
            return True
        if self._keyword_test and hasattr(self._keyword_test, keyword):
            return True
        return False
