"""
智能表单填充模块

提供：
- 智能识别表单字段
- 自动匹配字段类型
- 支持下拉框、复选框、日期选择器
- 表单验证
"""
import asyncio
from typing import Optional, Dict, Any, List, Union
from dataclasses import dataclass
from loguru import logger
import re


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


class SmartFormFiller:
    """
    智能表单填充器

    自动识别表单字段类型并智能填充。
    """

    # 字段类型映射
    FIELD_TYPE_PATTERNS = {
        "email": [
            r"email",
            r"mail",
            r"e-mail",
        ],
        "phone": [
            r"phone",
            r"mobile",
            r"cell",
            r"tel",
        ],
        "password": [
            r"password",
            r"pwd",
            r"passwd",
            r"pass",
        ],
        "username": [
            r"username",
            r"user",
            r"login",
            r"account",
        ],
        "name": [
            r"^[fF]irst[Nn]ame$",
            r"^[lL]ast[Nn]ame$",
            r"^[Ff]ull[Nn]ame$",
            r"^[Nn]ame$",
        ],
        "address": [
            r"address",
            r"street",
            r"city",
            r"zip",
            r"postal",
        ],
        "date": [
            r"date",
            r"birthday",
            r"birth",
        ],
        "number": [
            r"age",
            r"amount",
            r"quantity",
            r"number",
        ],
        "url": [
            r"url",
            r"website",
            r"link",
        ],
    }

    def __init__(self, page):
        self._page = page
        self._fields: Dict[str, FieldInfo] = {}

    async def detect_fields(self) -> List[FieldInfo]:
        """
        检测页面上的所有表单字段

        Returns:
            List[FieldInfo]: 字段信息列表
        """
        fields = []

        # 检测输入框
        inputs = await self._detect_inputs()
        fields.extend(inputs)

        # 检测选择框
        selects = await self._detect_selects()
        fields.extend(selects)

        # 检测复选框和单选框
        checkboxes = await self._detect_checkboxes()
        fields.extend(checkboxes)

        # 检测文本域
        textareas = await self._detect_textareas()
        fields.extend(textareas)

        self._fields = {f.name: f for f in fields}
        return fields

    async def _detect_inputs(self) -> List[FieldInfo]:
        """检测输入框"""
        fields = []
        locators = self._page.locator("input:not([type='hidden']):not([type='submit']):not([type='button'])")

        count = await locators.count()
        for i in range(count):
            locator = locators.nth(i)
            try:
                name = await locator.get_attribute("name") or f"input_{i}"
                input_type = await locator.get_attribute("type") or "text"
                placeholder = await locator.get_attribute("placeholder")
                required = await locator.get_attribute("required") is not None

                # 获取关联的 label
                label = await self._get_label(locator, name)

                field_info = FieldInfo(
                    name=name,
                    field_type=self._guess_field_type(name, placeholder, label, input_type),
                    value=None,
                    locator=locator,
                    label=label,
                    placeholder=placeholder,
                    required=required,
                )
                fields.append(field_info)
            except:
                continue

        return fields

    async def _detect_selects(self) -> List[FieldInfo]:
        """检测下拉框"""
        fields = []
        locators = self._page.locator("select")

        count = await locators.count()
        for i in range(count):
            locator = locators.nth(i)
            try:
                name = await locator.get_attribute("name") or f"select_{i}"
                required = await locator.get_attribute("required") is not None
                label = await self._get_label(locator, name)

                field_info = FieldInfo(
                    name=name,
                    field_type="select",
                    value=None,
                    locator=locator,
                    label=label,
                    required=required,
                )
                fields.append(field_info)
            except:
                continue

        return fields

    async def _detect_checkboxes(self) -> List[FieldInfo]:
        """检测复选框和单选框"""
        fields = []

        # 复选框
        checkboxes = self._page.locator("input[type='checkbox']")
        count = await checkboxes.count()
        for i in range(count):
            locator = checkboxes.nth(i)
            try:
                name = await locator.get_attribute("name") or f"checkbox_{i}"
                label = await self._get_label(locator, name)

                field_info = FieldInfo(
                    name=name,
                    field_type="checkbox",
                    value=False,
                    locator=locator,
                    label=label,
                )
                fields.append(field_info)
            except:
                continue

        # 单选框
        radios = self._page.locator("input[type='radio']")
        count = await radios.count()
        for i in range(count):
            locator = radios.nth(i)
            try:
                name = await locator.get_attribute("name") or f"radio_{i}"
                label = await self._get_label(locator, name)

                field_info = FieldInfo(
                    name=name,
                    field_type="radio",
                    value=None,
                    locator=locator,
                    label=label,
                )
                fields.append(field_info)
            except:
                continue

        return fields

    async def _detect_textareas(self) -> List[FieldInfo]:
        """检测文本域"""
        fields = []
        locators = self._page.locator("textarea")

        count = await locators.count()
        for i in range(count):
            locator = locators.nth(i)
            try:
                name = await locator.get_attribute("name") or f"textarea_{i}"
                placeholder = await locator.get_attribute("placeholder")
                required = await locator.get_attribute("required") is not None
                label = await self._get_label(locator, name)

                field_info = FieldInfo(
                    name=name,
                    field_type="textarea",
                    value=None,
                    locator=locator,
                    label=label,
                    placeholder=placeholder,
                    required=required,
                )
                fields.append(field_info)
            except:
                continue

        return fields

    async def _get_label(self, locator, name: str) -> Optional[str]:
        """获取字段的 label"""
        # 尝试通过 for 属性查找
        field_id = await locator.get_attribute("id")
        if field_id:
            label_locator = self._page.locator(f"label[for='{field_id}']")
            if await label_locator.count() > 0:
                return await label_locator.text_content()

        # 尝试查找父 label
        parent_label = locator.locator("xpath=ancestor::label")
        if await parent_label.count() > 0:
            return await parent_label.text_content()

        # 尝试查找前面的 label
        prev_label = locator.locator("xpath=preceding-sibling::label")
        if await prev_label.count() > 0:
            return await prev_label.text_content()

        return None

    def _guess_field_type(
        self,
        name: str,
        placeholder: Optional[str],
        label: Optional[str],
        input_type: str,
    ) -> str:
        """猜测字段类型"""
        # 检查输入类型
        if input_type in ("email", "password", "number", "date", "url", "tel"):
            return input_type

        # 检查名称
        text_to_check = f"{name} {placeholder or ''} {label or ''}".lower()

        for field_type, patterns in self.FIELD_TYPE_PATTERNS.items():
            for pattern in patterns:
                if re.search(pattern, text_to_check, re.IGNORECASE):
                    return field_type

        return "text"

    async def fill_field(self, name: str, value: Any) -> bool:
        """
        填充指定字段

        Args:
            name: 字段名称
            value: 值

        Returns:
            bool: 是否成功
        """
        if name not in self._fields:
            logger.warning(f"字段不存在: {name}")
            return False

        field = self._fields[name]

        try:
            if field.field_type == "select":
                await field.locator.select_option(value)
            elif field.field_type == "checkbox":
                if value:
                    await field.locator.check()
                else:
                    await field.locator.uncheck()
            elif field.field_type == "radio":
                await field.locator.check()
            elif field.field_type == "file":
                await field.locator.set_input_files(value)
            else:
                await field.locator.fill(str(value))

            field.value = value
            logger.debug(f"填充字段: {name} = {value}")
            return True
        except Exception as e:
            logger.error(f"填充字段失败 {name}: {e}")
            return False

    async def fill_form(self, data: Dict[str, Any]) -> Dict[str, bool]:
        """
        批量填充表单

        Args:
            data: 字段名 -> 值 的映射

        Returns:
            Dict[str, bool]: 每个字段的填充结果
        """
        results = {}

        for name, value in data.items():
            results[name] = await self.fill_field(name, value)

        return results

    async def auto_fill(
        self,
        data: Optional[Dict[str, Any]] = None,
        fill_required_only: bool = False,
    ) -> Dict[str, Any]:
        """
        自动填充表单

        根据字段类型自动生成值或使用提供的值。

        Args:
            data: 可选的字段值映射
            fill_required_only: 是否只填充必填字段

        Returns:
            Dict[str, Any]: 填充的值
        """
        if not self._fields:
            await self.detect_fields()

        data = data or {}
        filled = {}

        for name, field in self._fields.items():
            # 跳过非必填字段
            if fill_required_only and not field.required:
                continue

            # 使用提供的值或自动生成
            if name in data:
                value = data[name]
            else:
                value = self._generate_value(field)

            if value is not None:
                success = await self.fill_field(name, value)
                if success:
                    filled[name] = value

        return filled

    def _generate_value(self, field: FieldInfo) -> Optional[Any]:
        """根据字段类型生成值"""
        import random
        import string

        field_type = field.field_type

        if field_type == "email":
            return f"test_{random.randint(1000, 9999)}@example.com"
        elif field_type == "phone":
            return f"1{random.randint(3000000000, 9999999999)}"
        elif field_type == "password":
            return "Test@123456"
        elif field_type == "username":
            return f"testuser_{random.randint(1000, 9999)}"
        elif field_type == "name":
            if "first" in field.name.lower():
                return random.choice(["张", "李", "王", "刘"]) + random.choice(["伟", "芳", "娜", "敏"])
            elif "last" in field.name.lower():
                return random.choice(["明", "华", "强", "丽"])
            else:
                return "测试用户"
        elif field_type == "number":
            return str(random.randint(1, 100))
        elif field_type == "url":
            return "https://example.com"
        elif field_type == "date":
            return "2000-01-01"
        elif field_type in ("text", "textarea"):
            return f"测试内容_{random.randint(100, 999)}"
        elif field_type == "checkbox":
            return True
        elif field_type == "select":
            return None  # 需要手动选择

        return None

    async def validate_form(self) -> Dict[str, List[str]]:
        """
        验证表单

        Returns:
            Dict[str, List[str]]: 字段名 -> 错误列表
        """
        errors = {}

        for name, field in self._fields.items():
            field_errors = []

            # 检查必填字段
            if field.required:
                if field.field_type == "checkbox":
                    is_checked = await field.locator.is_checked()
                    if not is_checked:
                        field_errors.append("此字段为必填项")
                elif field.field_type == "select":
                    value = await field.locator.input_value()
                    if not value:
                        field_errors.append("请选择一个选项")
                else:
                    value = await field.locator.input_value()
                    if not value:
                        field_errors.append("此字段为必填项")

            # 检查邮箱格式
            if field.field_type == "email" and field.value:
                if not re.match(r"^[^@]+@[^@]+\.[^@]+$", str(field.value)):
                    field_errors.append("邮箱格式不正确")

            # 检查手机号格式
            if field.field_type == "phone" and field.value:
                if not re.match(r"^1\d{10}$", str(field.value)):
                    field_errors.append("手机号格式不正确")

            if field_errors:
                errors[name] = field_errors

        return errors

    async def clear_form(self) -> None:
        """清空表单"""
        for name, field in self._fields.items():
            try:
                if field.field_type == "checkbox":
                    await field.locator.uncheck()
                elif field.field_type == "select":
                    await field.locator.select_option(index=0)
                else:
                    await field.locator.clear()
                field.value = None
            except:
                continue

        logger.info("表单已清空")

    async def get_form_data(self) -> Dict[str, Any]:
        """
        获取表单数据

        Returns:
            Dict[str, Any]: 字段名 -> 值
        """
        data = {}

        for name, field in self._fields.items():
            try:
                if field.field_type == "checkbox":
                    data[name] = await field.locator.is_checked()
                elif field.field_type == "select":
                    data[name] = await field.locator.input_value()
                else:
                    data[name] = await field.locator.input_value()
            except:
                data[name] = None

        return data
