"""
元素代理类 - 实现极简语法和链式调用

借鉴 DrissionPage 的设计理念，提供更简洁的元素操作方式。

示例:
    async with EasyBrowser() as browser:
        # 极简语法
        await browser('button.submit').click()      # CSS
        await browser('//div[@id="x"]').click()     # XPath
        await browser('text=登录').click()          # 文本

        # 链式调用
        text = await browser('div.card').text
        await browser('input').fill('hello')

        # 相对定位
        parent = browser('div.container')
        await parent.child('button').click()
"""
from typing import Optional, Union, TYPE_CHECKING
from playwright.async_api import Locator, Page

if TYPE_CHECKING:
    from .relative import RelativeLocator


class Element:
    """
    元素代理类

    包装 Playwright Locator，提供极简语法和链式调用。
    支持自动检测选择器类型（CSS/XPath/文本等）。
    """

    def __init__(
        self,
        locator: Locator,
        page: Page,
        selector: str = "",
    ):
        """
        初始化元素代理

        Args:
            locator: Playwright Locator 对象
            page: Page 对象
            selector: 原始选择器（用于日志和调试）
        """
        self._locator = locator
        self._page = page
        self._selector = selector
        self._relative: Optional["RelativeLocator"] = None

    # ==================== 属性访问 ====================

    @property
    def locator(self) -> Locator:
        """获取底层 Locator 对象"""
        return self._locator

    @property
    def page(self) -> Page:
        """获取 Page 对象"""
        return self._page

    @property
    async def text(self) -> str:
        """获取元素文本内容"""
        return await self._locator.text_content() or ""

    @property
    async def inner_text(self) -> str:
        """获取元素内部文本"""
        return await self._locator.inner_text()

    @property
    async def value(self) -> str:
        """获取输入框的值"""
        return await self._locator.input_value()

    @property
    async def html(self) -> str:
        """获取元素内部 HTML"""
        return await self._locator.inner_html()

    @property
    async def attribute(self) -> dict:
        """获取元素所有属性"""
        return await self._locator.evaluate("el => Object.assign({}, el.attributes)")

    @property
    async def href(self) -> Optional[str]:
        """获取链接地址（仅对 <a> 标签有效）"""
        return await self._locator.get_attribute("href")

    @property
    async def src(self) -> Optional[str]:
        """获取资源地址（仅对 img/video/audio 等有效）"""
        return await self._locator.get_attribute("src")

    @property
    async def class_name(self) -> Optional[str]:
        """获取 class 属性"""
        return await self._locator.get_attribute("class")

    @property
    async def id(self) -> Optional[str]:
        """获取 id 属性"""
        return await self._locator.get_attribute("id")

    @property
    async def tag_name(self) -> str:
        """获取标签名"""
        return await self._locator.evaluate("el => el.tagName.toLowerCase()")

    # ==================== 操作方法 ====================

    async def click(
        self,
        force: bool = False,
        timeout: Optional[float] = None,
        no_wait_after: bool = False,
        position: Optional[dict] = None,
    ) -> "Element":
        """
        点击元素

        Args:
            force: 是否强制点击（跳过可操作性检查）
            timeout: 超时时间
            no_wait_after: 点击后是否不等待
            position: 点击位置 {"x": 0, "y": 0}

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.click(
            force=force,
            timeout=timeout * 1000 if timeout else None,
            no_wait_after=no_wait_after,
            position=position,
        )
        return self

    async def dblclick(
        self,
        force: bool = False,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        双击元素

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.dblclick(
            force=force,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def fill(
        self,
        value: str,
        force: bool = False,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        填充输入框

        Args:
            value: 要填充的值
            force: 是否强制填充
            timeout: 超时时间

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.fill(
            value,
            force=force,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def type(
        self,
        text: str,
        delay: int = 50,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        逐字输入（模拟打字）

        Args:
            text: 要输入的文本
            delay: 每个字符之间的延迟（毫秒）
            timeout: 超时时间

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.type(
            text,
            delay=delay,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def clear(self, timeout: Optional[float] = None) -> "Element":
        """
        清空输入框

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.clear(timeout=timeout * 1000 if timeout else None)
        return self

    async def check(
        self,
        force: bool = False,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        勾选复选框

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.check(
            force=force,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def uncheck(
        self,
        force: bool = False,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        取消勾选复选框

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.uncheck(
            force=force,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def select_option(
        self,
        value: Optional[Union[str, list]] = None,
        label: Optional[Union[str, list]] = None,
        index: Optional[Union[int, list]] = None,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        选择下拉框选项

        Args:
            value: 按 value 属性选择
            label: 按 label 选择
            index: 按索引选择

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.select_option(
            value=value,
            label=label,
            index=index,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def hover(
        self,
        force: bool = False,
        position: Optional[dict] = None,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        鼠标悬停

        Args:
            force: 是否强制悬停
            position: 悬停位置
            timeout: 超时时间

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.hover(
            force=force,
            position=position,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def focus(self, timeout: Optional[float] = None) -> "Element":
        """
        聚焦元素

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.focus(timeout=timeout * 1000 if timeout else None)
        return self

    async def blur(self) -> "Element":
        """
        失焦元素

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.blur()
        return self

    async def press(
        self,
        key: str,
        delay: Optional[int] = None,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        按键

        Args:
            key: 按键名称（如 Enter, Tab, Escape 等）
            delay: 按键延迟
            timeout: 超时时间

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.press(
            key,
            delay=delay,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def scroll_into_view(
        self,
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        滚动到元素可见

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.scroll_into_view_if_needed(
            timeout=timeout * 1000 if timeout else None
        )
        return self

    # ==================== 等待方法 ====================

    async def wait_for(
        self,
        state: str = "visible",
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        等待元素状态

        Args:
            state: 状态 (visible/hidden/attached/detached)
            timeout: 超时时间

        Returns:
            Element: 返回自身，支持链式调用
        """
        await self._locator.wait_for(
            state=state,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    async def wait_until_visible(self, timeout: Optional[float] = None) -> "Element":
        """等待元素可见"""
        return await self.wait_for("visible", timeout)

    async def wait_until_hidden(self, timeout: Optional[float] = None) -> "Element":
        """等待元素隐藏"""
        return await self.wait_for("hidden", timeout)

    async def wait_until_attached(self, timeout: Optional[float] = None) -> "Element":
        """等待元素附加到 DOM"""
        return await self.wait_for("attached", timeout)

    # ==================== 状态检查 ====================

    async def is_visible(self) -> bool:
        """元素是否可见"""
        return await self._locator.is_visible()

    async def is_hidden(self) -> bool:
        """元素是否隐藏"""
        return await self._locator.is_hidden()

    async def is_enabled(self) -> bool:
        """元素是否可用"""
        return await self._locator.is_enabled()

    async def is_disabled(self) -> bool:
        """元素是否禁用"""
        return await self._locator.is_disabled()

    async def is_editable(self) -> bool:
        """元素是否可编辑"""
        return await self._locator.is_editable()

    async def is_checked(self) -> bool:
        """复选框是否被勾选"""
        return await self._locator.is_checked()

    async def count(self) -> int:
        """匹配元素数量"""
        return await self._locator.count()

    # ==================== 相对定位 ====================

    @property
    def relative(self) -> "RelativeLocator":
        """
        获取相对定位器

        Returns:
            RelativeLocator: 相对定位器实例
        """
        if self._relative is None:
            from .relative import RelativeLocator
            self._relative = RelativeLocator(self._locator, self._page)
        return self._relative

    def parent(self, level: int = 1) -> "Element":
        """
        获取父元素

        Args:
            level: 向上层级（默认1为直接父元素）

        Returns:
            Element: 父元素
        """
        return self.relative.parent(level)

    def child(self, selector: Optional[str] = None, index: int = 0) -> "Element":
        """
        获取子元素

        Args:
            selector: 子元素选择器（可选）
            index: 子元素索引

        Returns:
            Element: 子元素
        """
        return self.relative.child(selector, index)

    def sibling(
        self,
        selector: Optional[str] = None,
        direction: str = "next"
    ) -> "Element":
        """
        获取兄弟元素

        Args:
            selector: 兄弟元素选择器（可选）
            direction: 方向 (next/prev)

        Returns:
            Element: 兄弟元素
        """
        return self.relative.sibling(selector, direction)

    def next_sibling(self, selector: Optional[str] = None) -> "Element":
        """获取下一个兄弟元素"""
        return self.sibling(selector, "next")

    def prev_sibling(self, selector: Optional[str] = None) -> "Element":
        """获取上一个兄弟元素"""
        return self.sibling(selector, "prev")

    def ancestor(self, selector: str) -> "Element":
        """
        获取祖先元素

        Args:
            selector: 祖先元素选择器

        Returns:
            Element: 祖先元素
        """
        return self.relative.ancestor(selector)

    # ==================== 筛选方法 ====================

    def nth(self, index: int) -> "Element":
        """
        获取第 N 个匹配元素

        Args:
            index: 索引（从 0 开始）

        Returns:
            Element: 第 N 个元素
        """
        return Element(self._locator.nth(index), self._page, f"{self._selector}:nth({index})")

    def first(self) -> "Element":
        """获取第一个匹配元素"""
        return Element(self._locator.first, self._page, f"{self._selector}:first")

    def last(self) -> "Element":
        """获取最后一个匹配元素"""
        return Element(self._locator.last, self._page, f"{self._selector}:last")

    def filter(
        self,
        has_text: Optional[str] = None,
        has_not_text: Optional[str] = None,
        has: Optional["Element"] = None,
    ) -> "Element":
        """
        过滤元素

        Args:
            has_text: 必须包含的文本
            has_not_text: 不能包含的文本
            has: 必须包含的子元素

        Returns:
            Element: 过滤后的元素
        """
        locator = self._locator
        if has_text:
            locator = locator.filter(has_text=has_text)
        if has_not_text:
            locator = locator.filter(has_not_text=has_not_text)
        if has:
            locator = locator.filter(has=has._locator)
        return Element(locator, self._page, self._selector)

    # ==================== 属性操作 ====================

    async def get_attribute(self, name: str) -> Optional[str]:
        """
        获取属性值

        Args:
            name: 属性名

        Returns:
            属性值
        """
        return await self._locator.get_attribute(name)

    async def set_attribute(self, name: str, value: str) -> "Element":
        """
        设置属性值

        Args:
            name: 属性名
            value: 属性值

        Returns:
            Element: 返回自身
        """
        await self._locator.evaluate(
            f"el => el.setAttribute('{name}', '{value}')"
        )
        return self

    async def remove_attribute(self, name: str) -> "Element":
        """
        移除属性

        Args:
            name: 属性名

        Returns:
            Element: 返回自身
        """
        await self._locator.evaluate(f"el => el.removeAttribute('{name}')")
        return self

    async def add_class(self, class_name: str) -> "Element":
        """添加 CSS 类"""
        await self._locator.evaluate(
            f"el => el.classList.add('{class_name}')"
        )
        return self

    async def remove_class(self, class_name: str) -> "Element":
        """移除 CSS 类"""
        await self._locator.evaluate(
            f"el => el.classList.remove('{class_name}')"
        )
        return self

    # ==================== 表单操作 ====================

    async def submit(self) -> "Element":
        """提交表单"""
        await self._locator.evaluate("el => el.form?.submit()")
        return self

    async def set_input_files(
        self,
        files: Union[str, list],
        timeout: Optional[float] = None,
    ) -> "Element":
        """
        设置文件输入

        Args:
            files: 文件路径或路径列表
            timeout: 超时时间

        Returns:
            Element: 返回自身
        """
        await self._locator.set_input_files(
            files,
            timeout=timeout * 1000 if timeout else None,
        )
        return self

    # ==================== 截图 ====================

    async def screenshot(
        self,
        path: Optional[str] = None,
        timeout: Optional[float] = None,
    ) -> bytes:
        """
        元素截图

        Args:
            path: 保存路径
            timeout: 超时时间

        Returns:
            bytes: 截图数据
        """
        return await self._locator.screenshot(
            path=path,
            timeout=timeout * 1000 if timeout else None,
        )

    # ==================== JavaScript 执行 ====================

    async def evaluate(self, expression: str) -> any:
        """
        在元素上执行 JavaScript

        Args:
            expression: JavaScript 表达式（el 为元素引用）

        Returns:
            执行结果
        """
        return await self._locator.evaluate(f"el => {expression}")

    async def evaluate_all(self, expression: str) -> any:
        """
        在所有匹配元素上执行 JavaScript

        Args:
            expression: JavaScript 表达式（els 为元素数组引用）

        Returns:
            执行结果
        """
        return await self._locator.evaluate_all(f"els => {expression}")

    # ==================== 魔术方法 ====================

    def __repr__(self) -> str:
        return f"<Element selector='{self._selector}'>"

    def __str__(self) -> str:
        return self._selector

    async def __aiter__(self):
        """支持异步迭代"""
        count = await self.count()
        for i in range(count):
            yield self.nth(i)


class ElementList:
    """
    元素列表类

    用于处理多个匹配元素的场景。
    """

    def __init__(self, locator: Locator, page: Page, selector: str = ""):
        self._locator = locator
        self._page = page
        self._selector = selector

    async def count(self) -> int:
        """元素数量"""
        return await self._locator.count()

    async def texts(self) -> list[str]:
        """获取所有元素文本"""
        return await self._locator.all_text_contents()

    async def attributes(self, name: str) -> list[Optional[str]]:
        """获取所有元素的某个属性"""
        return await self._locator.evaluate_all(
            f"els => els.map(el => el.getAttribute('{name}'))"
        )

    def __getitem__(self, index: int) -> Element:
        """通过索引获取元素"""
        return Element(self._locator.nth(index), self._page, f"{self._selector}[{index}]")

    async def __aiter__(self):
        """支持异步迭代"""
        count = await self.count()
        for i in range(count):
            yield self[i]

    async def __len__(self) -> int:
        return await self.count()

    def __repr__(self) -> str:
        return f"<ElementList selector='{self._selector}'>"
