from __future__ import annotations

from typing import Optional, Any

from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


def 等待元素可点击(
    驱动器对象: Any,
    定位方法: str,
    定位值: str,
    超时时间: int | float = 30,
) -> Optional[WebElement]:
    """
    等待指定元素达到“可点击”状态，并返回该元素对象。

    本函数基于 Selenium 的显式等待机制实现，适用于页面异步加载、
    动态渲染、元素延迟可用等场景。函数会在给定超时时间内持续轮询，
    直到目标元素满足“存在、可见且可点击”条件后返回对应元素对象。

    为保持工具库调用风格统一，本函数内部不会主动抛出异常：
        - 等待成功时，返回可点击的元素对象
        - 等待失败、参数异常或其他异常时，返回 None

    Args:
        驱动器对象 (Any):
            Selenium 浏览器驱动对象，例如 Chrome、Edge 或其他 WebDriver 实例。

        定位方法 (str):
            元素定位方式名称，不需要写 `By.` 前缀，函数内部会自动转换。
            常见可用值包括：
                - "ID"
                - "XPATH"
                - "NAME"
                - "CSS_SELECTOR"
                - "CLASS_NAME"
                - "TAG_NAME"
                - "LINK_TEXT"
                - "PARTIAL_LINK_TEXT"

        定位值 (str):
            与定位方法对应的具体定位内容，例如元素 ID、XPath 表达式、
            CSS 选择器等。

        超时时间 (int | float, optional):
            最大等待秒数，默认值为 30。

    Returns:
        Optional[WebElement]:
            等待成功时返回 Selenium 的 WebElement 元素对象；
            若等待超时、定位方式无效、参数不合法或执行过程中发生异常，
            返回 None。

    Notes:
        - “可点击”通常表示元素已出现在页面中、处于可见状态，并可执行点击操作。
        - 本函数不会额外执行固定睡眠等待，以避免无意义拖慢自动化流程。
        - 若目标页面存在遮挡层、动画过渡或特殊前端拦截机制，即使返回元素对象，
          后续点击时仍可能失败，此时应结合点击函数做进一步兜底处理。
    """
    try:
        if 驱动器对象 is None:
            return None

        if not isinstance(定位方法, str) or not 定位方法.strip():
            return None

        if not isinstance(定位值, str) or not 定位值.strip():
            return None

        if not isinstance(超时时间, (int, float)) or 超时时间 <= 0:
            return None

        定位方法文本 = 定位方法.strip().upper()
        定位方法常量 = getattr(By, 定位方法文本, None)
        if 定位方法常量 is None:
            return None

        return WebDriverWait(驱动器对象, 超时时间).until(
            EC.element_to_be_clickable((定位方法常量, 定位值))
        )
    except Exception:
        return None
