"""
表单处理模块
智能表单填充、验证、提交
"""
import re
from typing import Optional, Dict, Any, List
from playwright.async_api import Page, Locator
from loguru import logger
from dataclasses import dataclass


@dataclass
class FieldInfo:
    """字段信息"""
    name: str
    type: str
    selector: str
    value: Any
    required: bool = False
    label: Optional[str] = None


class FormHandler:
    """
    表单处理器
    智能表单填充、验证、提交
    """
    
    # 字段类型映射
    FIELD_PATTERNS = {
        "email": [
            r"email",
            r"e-mail",
            r"mail",
        ],
        "phone": [
            r"phone",
            r"mobile",
            r"tel",
            r"cell",
            r"手机",
            r"电话",
        ],
        "password": [
            r"password",
            r"passwd",
            r"pwd",
            r"密码",
        ],
        "name": [
            r"name",
            r"姓名",
            r"名字",
            r"用户名",
            r"username",
        ],
        "address": [
            r"address",
            r"地址",
            r"street",
            r"街道",
        ],
        "city": [
            r"city",
            r"城市",
        ],
        "zip": [
            r"zip",
            r"postal",
            r"邮编",
        ],
        "card": [
            r"card",
            r"credit",
            r"银行卡",
        ],
        "captcha": [
            r"captcha",
            r"验证码",
            r"code",
        ],
    }
    
    def __init__(self, page: Page):
        self.page = page
    
    async def fill(
        self,
        data: Dict[str, Any],
        form_selector: str = "form",
        auto_detect: bool = True,
    ) -> bool:
        """
        智能填充表单
        
        Args:
            data: 数据字典
            form_selector: 表单选择器
            auto_detect: 是否自动检测字段类型
            
        Returns:
            bool: 是否成功
        """
        logger.info(f"填充表单: form={form_selector}, fields={len(data)}")
        
        form = self.page.locator(form_selector)
        
        for field_name, value in data.items():
            try:
                # 尝试多种方式找到字段
                field = await self._find_field(form, field_name, auto_detect)
                
                if field:
                    await self._fill_field(field, value)
                    logger.debug(f"字段填充成功: {field_name}")
                else:
                    logger.warning(f"未找到字段: {field_name}")
                    
            except Exception as e:
                logger.error(f"字段填充失败: {field_name}, error={e}")
        
        logger.info("表单填充完成")
        return True
    
    async def _find_field(
        self,
        form: Locator,
        field_name: str,
        auto_detect: bool,
    ) -> Optional[Locator]:
        """查找表单字段"""
        selectors = [
            # 1. 通过 name 属性
            f"[name='{field_name}']",
            f"[name*='{field_name}']",
            # 2. 通过 id
            f"#{field_name}",
            f"[id*='{field_name}']",
            # 3. 通过 placeholder
            f"[placeholder*='{field_name}']",
            # 4. 通过 aria-label
            f"[aria-label*='{field_name}']",
            # 5. 通过 label
            f"label:has-text('{field_name}') + input",
            f"label:has-text('{field_name}') ~ input",
        ]
        
        for selector in selectors:
            try:
                field = form.locator(selector)
                if await field.count() > 0:
                    return field.first
            except:
                continue
        
        return None
    
    async def _fill_field(self, field: Locator, value: Any):
        """填充字段"""
        tag = await field.evaluate("el => el.tagName.toLowerCase()")
        input_type = await field.get_attribute("type") or "text"
        
        if tag == "select":
            # 下拉框
            await field.select_option(value)
        elif input_type == "checkbox":
            # 复选框
            if value:
                await field.check()
            else:
                await field.uncheck()
        elif input_type == "radio":
            # 单选框
            await field.check()
        elif input_type == "file":
            # 文件上传
            await field.set_input_files([value])
        else:
            # 文本输入
            await field.clear()
            await field.fill(str(value))
    
    async def fill_by_label(
        self,
        label_text: str,
        value: Any,
    ) -> bool:
        """
        通过标签文本填充字段
        
        Args:
            label_text: 标签文本
            value: 值
            
        Returns:
            bool: 是否成功
        """
        try:
            # 通过 label 关联
            field = self.page.get_by_label(label_text)
            if await field.count() > 0:
                await self._fill_field(field, value)
                return True
            
            # 通过相邻 label
            field = self.page.locator(f"label:has-text('{label_text}') + input")
            if await field.count() > 0:
                await self._fill_field(field.first, value)
                return True
            
            # 通过 placeholder
            field = self.page.get_by_placeholder(label_text)
            if await field.count() > 0:
                await self._fill_field(field, value)
                return True
            
            logger.warning(f"未找到标签对应的字段: {label_text}")
            return False
            
        except Exception as e:
            logger.error(f"通过标签填充失败: {e}")
            return False
    
    async def fill_login_form(
        self,
        username: str,
        password: str,
        username_selector: Optional[str] = None,
        password_selector: Optional[str] = None,
        submit_selector: Optional[str] = None,
    ) -> bool:
        """
        填充登录表单
        
        Args:
            username: 用户名
            password: 密码
            username_selector: 用户名字段选择器
            password_selector: 密码字段选择器
            submit_selector: 提交按钮选择器
            
        Returns:
            bool: 是否成功
        """
        logger.info("填充登录表单")
        
        # 查找用户名字段
        if username_selector:
            username_field = self.page.locator(username_selector)
        else:
            username_field = await self._find_field(
                self.page.locator("body"),
                "username",
                True
            ) or await self._find_field(
                self.page.locator("body"),
                "email",
                True
            )
        
        # 查找密码字段
        if password_selector:
            password_field = self.page.locator(password_selector)
        else:
            password_field = self.page.locator("input[type='password']")
        
        # 填充
        if username_field:
            await username_field.fill(username)
        if await password_field.count() > 0:
            await password_field.first.fill(password)
        
        # 提交
        if submit_selector:
            await self.page.click(submit_selector)
            logger.info("登录表单已提交")
        
        return True
    
    async def get_values(self, form_selector: str = "form") -> Dict[str, str]:
        """
        获取表单所有字段的值
        
        Args:
            form_selector: 表单选择器
            
        Returns:
            Dict: 字段名和值
        """
        form = self.page.locator(form_selector)
        values = {}
        
        # 获取所有输入框
        inputs = form.locator("input, select, textarea")
        count = await inputs.count()
        
        for i in range(count):
            field = inputs.nth(i)
            
            name = await field.get_attribute("name")
            if not name:
                name = await field.get_attribute("id")
            
            if name:
                tag = await field.evaluate("el => el.tagName.toLowerCase()")
                input_type = await field.get_attribute("type") or "text"
                
                if input_type in ["checkbox", "radio"]:
                    values[name] = await field.is_checked()
                elif tag == "select":
                    values[name] = await field.input_value()
                else:
                    values[name] = await field.input_value()
        
        return values
    
    async def clear(self, form_selector: str = "form"):
        """
        清空表单
        
        Args:
            form_selector: 表单选择器
        """
        form = self.page.locator(form_selector)
        
        inputs = form.locator("input:not([type='checkbox']):not([type='radio']):not([type='file']), textarea")
        count = await inputs.count()
        
        for i in range(count):
            await inputs.nth(i).clear()
        
        # 取消勾选
        checkboxes = form.locator("input[type='checkbox']:checked, input[type='radio']:checked")
        count = await checkboxes.count()
        
        for i in range(count):
            await checkboxes.nth(i).uncheck()
        
        logger.info("表单已清空")
    
    async def validate(
        self,
        form_selector: str = "form",
        required_fields: Optional[List[str]] = None,
    ) -> Dict[str, Any]:
        """
        验证表单
        
        Args:
            form_selector: 表单选择器
            required_fields: 必填字段列表
            
        Returns:
            Dict: 验证结果
        """
        result = {
            "valid": True,
            "errors": [],
        }
        
        form = self.page.locator(form_selector)
        
        # 检查必填字段
        if required_fields:
            for field_name in required_fields:
                field = await self._find_field(form, field_name, False)
                if field:
                    value = await field.input_value()
                    if not value:
                        result["valid"] = False
                        result["errors"].append({
                            "field": field_name,
                            "error": "字段不能为空"
                        })
        
        # 检查 HTML5 验证
        invalid_fields = form.locator(":invalid")
        count = await invalid_fields.count()
        
        if count > 0:
            result["valid"] = False
            for i in range(count):
                field = invalid_fields.nth(i)
                name = await field.get_attribute("name") or await field.get_attribute("id")
                validation_msg = await field.evaluate("el => el.validationMessage")
                result["errors"].append({
                    "field": name,
                    "error": validation_msg
                })
        
        return result
