"""
页面对象模型(POM)支持
设计模式，让测试代码更易维护
"""
from typing import Optional, Type, Dict, Any, Callable
from playwright.async_api import Page, Locator
from loguru import logger
from dataclasses import dataclass, field
from functools import wraps
import inspect


@dataclass
class ElementLocator:
    """元素定位器"""
    selector: str
    name: Optional[str] = None
    description: Optional[str] = None
    wait_condition: Optional[str] = None
    timeout: float = 10.0


class PageElement:
    """
    页面元素基类
    用于POM模式中定义页面元素
    """
    
    def __init__(self, selector: str, name: str = "", timeout: float = 10.0):
        self.selector = selector
        self.name = name or selector
        self.timeout = timeout
        self._page: Optional[Page] = None
        self._locator: Optional[Locator] = None
    
    def bind(self, page: Page):
        """绑定页面对象"""
        self._page = page
        self._locator = page.locator(self.selector)
        return self
    
    @property
    def locator(self) -> Locator:
        """获取Locator"""
        if self._locator is None:
            raise RuntimeError("元素未绑定页面，请先调用 bind() 方法")
        return self._locator
    
    async def click(self, **kwargs):
        """点击"""
        await self.locator.click(**kwargs)
        logger.debug(f"点击元素: {self.name}")
    
    async def fill(self, value: str, **kwargs):
        """填充"""
        await self.locator.fill(value, **kwargs)
        logger.debug(f"填充元素: {self.name} = {value[:20]}...")
    
    async def text(self) -> str:
        """获取文本"""
        text = await self.locator.text_content()
        return text or ""
    
    async def value(self) -> str:
        """获取值"""
        return await self.locator.input_value()
    
    async def is_visible(self) -> bool:
        """是否可见"""
        return await self.locator.is_visible()
    
    async def is_enabled(self) -> bool:
        """是否可用"""
        return await self.locator.is_enabled()
    
    async def wait_for(self, state: str = "visible"):
        """等待"""
        await self.locator.wait_for(state=state, timeout=self.timeout * 1000)
        logger.debug(f"等待元素: {self.name}, state={state}")


class BasePage:
    """
    页面基类
    所有页面对象继承此类
    """
    
    # 页面URL（子类可覆盖）
    url: str = ""
    
    def __init__(self, page: Page):
        self.page = page
        self._bind_elements()
    
    def _bind_elements(self):
        """绑定所有PageElement属性"""
        for name, attr in inspect.getmembers(self.__class__):
            if isinstance(attr, PageElement):
                # 创建新实例并绑定
                element = PageElement(attr.selector, attr.name, attr.timeout)
                element.bind(self.page)
                setattr(self, name, element)
    
    async def navigate(self, url: Optional[str] = None):
        """导航到页面"""
        target_url = url or self.url
        if not target_url:
            raise ValueError("未设置页面URL")
        
        await self.page.goto(target_url)
        logger.info(f"导航到页面: {target_url}")
    
    async def wait_for_load(self, state: str = "load"):
        """等待页面加载"""
        await self.page.wait_for_load_state(state)
        logger.debug(f"页面加载完成: state={state}")
    
    async def is_on_page(self) -> bool:
        """检查是否在当前页面"""
        try:
            current_url = self.page.url
            return self.url in current_url if self.url else True
        except:
            return False
    
    async def take_screenshot(self, path: str):
        """截图"""
        await self.page.screenshot(path=path)
        logger.info(f"截图已保存: {path}")


def element(selector: str, name: str = "") -> PageElement:
    """
    元素装饰器
    用于在页面对象中定义元素
    
    Example:
        class LoginPage(BasePage):
            username = element("#username", "用户名")
            password = element("#password", "密码")
            submit = element("#submit", "提交按钮")
    """
    return PageElement(selector, name)


class PageFactory:
    """
    页面工厂
    用于创建和管理页面对象
    """
    
    def __init__(self, page: Page):
        self.page = page
        self._pages: Dict[str, BasePage] = {}
    
    def create(self, page_class: Type[BasePage]) -> BasePage:
        """
        创建页面对象
        
        Args:
            page_class: 页面类
            
        Returns:
            BasePage: 页面实例
        """
        page_name = page_class.__name__
        
        if page_name not in self._pages:
            self._pages[page_name] = page_class(self.page)
            logger.debug(f"创建页面对象: {page_name}")
        
        return self._pages[page_name]
    
    def get(self, page_name: str) -> Optional[BasePage]:
        """获取已创建的页面对象"""
        return self._pages.get(page_name)


class PageRegistry:
    """
    页面注册中心
    全局管理所有页面对象
    """
    
    _pages: Dict[str, Type[BasePage]] = {}
    
    @classmethod
    def register(cls, name: str):
        """注册页面装饰器"""
        def decorator(page_class: Type[BasePage]):
            cls._pages[name] = page_class
            logger.debug(f"注册页面: {name}")
            return page_class
        return decorator
    
    @classmethod
    def get(cls, name: str) -> Optional[Type[BasePage]]:
        """获取页面类"""
        return cls._pages.get(name)
    
    @classmethod
    def list_pages(cls) -> list:
        """列出所有注册的页面"""
        return list(cls._pages.keys())


def step(name: str = ""):
    """
    步骤装饰器
    用于标记测试步骤，自动记录日志
    
    Example:
        @step("输入用户名")
        async def input_username(self, username):
            await self.username.fill(username)
    """
    def decorator(func):
        @wraps(func)
        async def async_wrapper(*args, **kwargs):
            step_name = name or func.__name__
            logger.info(f"执行步骤: {step_name}")
            try:
                result = await func(*args, **kwargs)
                logger.info(f"步骤完成: {step_name}")
                return result
            except Exception as e:
                logger.error(f"步骤失败: {step_name}, error={e}")
                raise
        
        @wraps(func)
        def sync_wrapper(*args, **kwargs):
            step_name = name or func.__name__
            logger.info(f"执行步骤: {step_name}")
            try:
                result = func(*args, **kwargs)
                logger.info(f"步骤完成: {step_name}")
                return result
            except Exception as e:
                logger.error(f"步骤失败: {step_name}, error={e}")
                raise
        
        if inspect.iscoroutinefunction(func):
            return async_wrapper
        return sync_wrapper
    
    return decorator


# 示例：登录页面对象
class LoginPage(BasePage):
    """登录页面对象示例"""
    
    url = "https://example.com/login"
    
    # 定义页面元素
    username = element("#username", "用户名输入框")
    password = element("#password", "密码输入框")
    submit_btn = element("#submit", "登录按钮")
    error_msg = element(".error-message", "错误提示")
    
    @step("执行登录")
    async def login(self, username: str, password: str):
        """执行登录"""
        await self.username.fill(username)
        await self.password.fill(password)
        await self.submit_btn.click()
    
    @step("获取错误信息")
    async def get_error_message(self) -> str:
        """获取错误信息"""
        if await self.error_msg.is_visible():
            return await self.error_msg.text()
        return ""
