"""
智能等待模块
"""
import asyncio
import time
from enum import Enum
from typing import Optional, Callable, Any, Union
from dataclasses import dataclass
from playwright.async_api import Page, Locator, ElementHandle
from loguru import logger
from .config import get_config


class WaitCondition(Enum):
    """等待条件"""
    VISIBLE = "visible"           # 元素可见
    HIDDEN = "hidden"             # 元素隐藏
    ATTACHED = "attached"         # 元素附加到DOM
    DETACHED = "detached"         # 元素从DOM分离
    ENABLED = "enabled"           # 元素可用（非禁用）
    DISABLED = "disabled"         # 元素禁用
    EDITABLE = "editable"         # 元素可编辑
    READONLY = "readonly"         # 元素只读
    FOCUSED = "focused"           # 元素获得焦点
    STABLE = "stable"             # 元素稳定（位置不再变化）
    CLICKABLE = "clickable"       # 元素可点击
    TEXT_NOT_EMPTY = "text_not_empty"  # 文本不为空
    VALUE_NOT_EMPTY = "value_not_empty"  # 输入值不为空
    CUSTOM = "custom"             # 自定义条件


@dataclass
class WaitResult:
    """等待结果"""
    success: bool
    elapsed_time: float
    condition: WaitCondition
    element: Optional[Locator] = None
    error: Optional[str] = None


class SmartWait:
    """智能等待器"""
    
    def __init__(self, config=None):
        self.config = config or get_config()
    
    async def wait_for(
        self,
        locator: Locator,
        condition: WaitCondition = WaitCondition.VISIBLE,
        timeout: Optional[float] = None,
        custom_condition: Optional[Callable[[Locator], bool]] = None,
    ) -> WaitResult:
        """
        智能等待元素满足条件
        
        Args:
            locator: Playwright Locator 对象
            condition: 等待条件
            timeout: 超时时间（秒）
            custom_condition: 自定义条件函数
            
        Returns:
            WaitResult: 等待结果
        """
        timeout = timeout or self.config.default_timeout
        start_time = time.time()
        
        logger.debug(f"开始等待: condition={condition.value}, timeout={timeout}s")
        
        try:
            if condition == WaitCondition.VISIBLE:
                await locator.wait_for(state="visible", timeout=timeout * 1000)
                
            elif condition == WaitCondition.HIDDEN:
                await locator.wait_for(state="hidden", timeout=timeout * 1000)
                
            elif condition == WaitCondition.ATTACHED:
                await locator.wait_for(state="attached", timeout=timeout * 1000)
                
            elif condition == WaitCondition.DETACHED:
                await locator.wait_for(state="detached", timeout=timeout * 1000)
                
            elif condition == WaitCondition.ENABLED:
                await self._wait_for_enabled(locator, timeout)
                
            elif condition == WaitCondition.DISABLED:
                await self._wait_for_disabled(locator, timeout)
                
            elif condition == WaitCondition.STABLE:
                await self._wait_for_stable(locator, timeout)
                
            elif condition == WaitCondition.CLICKABLE:
                await self._wait_for_clickable(locator, timeout)
                
            elif condition == WaitCondition.TEXT_NOT_EMPTY:
                await self._wait_for_text(locator, timeout, empty=False)
                
            elif condition == WaitCondition.VALUE_NOT_EMPTY:
                await self._wait_for_value(locator, timeout)
                
            elif condition == WaitCondition.CUSTOM:
                if custom_condition is None:
                    raise ValueError("自定义条件需要提供 custom_condition 参数")
                await self._wait_for_custom(locator, custom_condition, timeout)
            
            elapsed = time.time() - start_time
            logger.debug(f"等待成功: condition={condition.value}, elapsed={elapsed:.2f}s")
            
            return WaitResult(
                success=True,
                elapsed_time=elapsed,
                condition=condition,
                element=locator
            )
            
        except Exception as e:
            elapsed = time.time() - start_time
            logger.error(f"等待失败: condition={condition.value}, error={e}")
            
            return WaitResult(
                success=False,
                elapsed_time=elapsed,
                condition=condition,
                error=str(e)
            )
    
    async def wait_for_any(
        self,
        locators: list[Locator],
        condition: WaitCondition = WaitCondition.VISIBLE,
        timeout: Optional[float] = None,
    ) -> tuple[int, WaitResult]:
        """
        等待任意一个元素满足条件
        
        Args:
            locators: Locator 列表
            condition: 等待条件
            timeout: 超时时间
            
        Returns:
            tuple: (元素索引, 等待结果)
        """
        timeout = timeout or self.config.default_timeout
        
        async def check_locator(index: int, locator: Locator):
            result = await self.wait_for(locator, condition, timeout)
            return index, result
        
        tasks = [check_locator(i, loc) for i, loc in enumerate(locators)]
        
        done, pending = await asyncio.wait(
            [asyncio.create_task(t) for t in tasks],
            timeout=timeout,
            return_when=asyncio.FIRST_COMPLETED
        )
        
        # 取消未完成的任务
        for task in pending:
            task.cancel()
        
        if done:
            return await done.pop()
        
        return -1, WaitResult(
            success=False,
            elapsed_time=timeout,
            condition=condition,
            error="所有元素等待超时"
        )
    
    async def wait_for_all(
        self,
        locators: list[Locator],
        condition: WaitCondition = WaitCondition.VISIBLE,
        timeout: Optional[float] = None,
    ) -> list[WaitResult]:
        """
        等待所有元素满足条件
        
        Args:
            locators: Locator 列表
            condition: 等待条件
            timeout: 超时时间
            
        Returns:
            list[WaitResult]: 所有元素的等待结果
        """
        timeout = timeout or self.config.default_timeout
        results = []
        
        for locator in locators:
            result = await self.wait_for(locator, condition, timeout)
            results.append(result)
            if not result.success:
                break
        
        return results
    
    async def wait_with_retry(
        self,
        action: Callable[[], Any],
        max_retries: int = 3,
        retry_interval: float = 1.0,
        timeout: Optional[float] = None,
    ) -> Any:
        """
        带重试的等待执行
        
        Args:
            action: 要执行的操作
            max_retries: 最大重试次数
            retry_interval: 重试间隔
            timeout: 单次操作超时
            
        Returns:
            操作结果
        """
        last_error = None
        
        for attempt in range(max_retries):
            try:
                logger.debug(f"执行操作: attempt={attempt + 1}/{max_retries}")
                if asyncio.iscoroutinefunction(action):
                    return await action()
                else:
                    return action()
            except Exception as e:
                last_error = e
                logger.warning(f"操作失败: attempt={attempt + 1}, error={e}")
                if attempt < max_retries - 1:
                    await asyncio.sleep(retry_interval)
        
        raise last_error or Exception("操作失败")
    
    # 私有方法
    
    async def _wait_for_enabled(self, locator: Locator, timeout: float):
        """等待元素可用"""
        start = time.time()
        while time.time() - start < timeout:
            if await locator.is_enabled():
                return
            await asyncio.sleep(self.config.poll_interval)
        raise TimeoutError(f"等待元素可用超时: {timeout}s")
    
    async def _wait_for_disabled(self, locator: Locator, timeout: float):
        """等待元素禁用"""
        start = time.time()
        while time.time() - start < timeout:
            if await locator.is_disabled():
                return
            await asyncio.sleep(self.config.poll_interval)
        raise TimeoutError(f"等待元素禁用超时: {timeout}s")
    
    async def _wait_for_stable(self, locator: Locator, timeout: float):
        """等待元素稳定"""
        threshold = self.config.stability_threshold
        last_box = None
        stable_count = 0
        
        start = time.time()
        while time.time() - start < timeout:
            try:
                box = await locator.bounding_box()
                if box is None:
                    stable_count = 0
                elif last_box is None:
                    stable_count = 1
                elif (abs(box['x'] - last_box['x']) < 1 and 
                      abs(box['y'] - last_box['y']) < 1):
                    stable_count += 1
                else:
                    stable_count = 0
                
                if stable_count >= threshold:
                    return
                    
                last_box = box
            except:
                stable_count = 0
            
            await asyncio.sleep(self.config.poll_interval)
        
        raise TimeoutError(f"等待元素稳定超时: {timeout}s")
    
    async def _wait_for_clickable(self, locator: Locator, timeout: float):
        """等待元素可点击"""
        start = time.time()
        while time.time() - start < timeout:
            if (await locator.is_visible() and 
                await locator.is_enabled() and
                not await locator.evaluate("el => el.hasAttribute('disabled')")):
                return
            await asyncio.sleep(self.config.poll_interval)
        raise TimeoutError(f"等待元素可点击超时: {timeout}s")
    
    async def _wait_for_text(self, locator: Locator, timeout: float, empty: bool = False):
        """等待文本内容"""
        start = time.time()
        while time.time() - start < timeout:
            text = await locator.text_content()
            if empty and (text is None or text.strip() == ""):
                return
            elif not empty and text and text.strip():
                return
            await asyncio.sleep(self.config.poll_interval)
        raise TimeoutError(f"等待文本超时: {timeout}s")
    
    async def _wait_for_value(self, locator: Locator, timeout: float):
        """等待输入值"""
        start = time.time()
        while time.time() - start < timeout:
            value = await locator.input_value()
            if value and value.strip():
                return
            await asyncio.sleep(self.config.poll_interval)
        raise TimeoutError(f"等待输入值超时: {timeout}s")
    
    async def _wait_for_custom(
        self, 
        locator: Locator, 
        condition: Callable[[Locator], bool],
        timeout: float
    ):
        """等待自定义条件"""
        start = time.time()
        while time.time() - start < timeout:
            if asyncio.iscoroutinefunction(condition):
                if await condition(locator):
                    return
            else:
                if condition(locator):
                    return
            await asyncio.sleep(self.config.poll_interval)
        raise TimeoutError(f"等待自定义条件超时: {timeout}s")
