#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：test_case.py
模块描述：测试用例 Pydantic 模式定义

作者：czj
创建日期：2026-07-09
最后修改：2026-07-09
"""

from datetime import datetime
from typing import Optional, List, Dict, Any, Union
from pydantic import BaseModel, Field, ConfigDict


def to_camel(string: str) -> str:
    """Convert snake_case to camelCase"""
    components = string.split('_')
    return components[0] + ''.join(x.title() for x in components[1:])


class StepDefinition(BaseModel):
    """
    测试步骤定义模式

    定义单个测试步骤的结构。

    Attributes:
        order (int): 步骤顺序
        name (str): 步骤名称
        action (str): 动作类型 (navigate/click/fill/assert等)
        params (dict): 动作参数
        expected (str): 预期结果
        actual (str): 实际结果（预留）
        selectors (Optional[List[str]]): 多选择器回退链
        page_key (Optional[str]): SelectorMapper 页面标识
        element_key (Optional[str]): 元素标识
        force (Optional[bool]): 强制点击
        wait_after (Optional[int]): 自定义操作后延时(ms)
        locator_type (Optional[str]): 定位类型: css/xpath/text/id/name
        locator_value (Optional[str]): 定位值
        semantic (Optional[dict]): 语义目标（语义化用例执行）：
            {type, text, contains, scope, role, nth, container, save_as}
        verify (Optional[dict]): 状态验证（防假通过）：
            {type, selector, text, timeout, must}
        run_on (Optional[str]): 执行时机: always/finally/on_failure/on_success（缺省 always）
    """

    order: int = Field(..., ge=1, description="步骤顺序")
    name: str = Field(..., min_length=1, max_length=200, description="步骤名称")
    action: str = Field(..., description="动作类型")
    params: Dict[str, Any] = Field(default_factory=dict, description="动作参数")
    expected: str = Field(default="", description="预期结果")
    actual: str = Field(default="", description="实际结果")
    selectors: Optional[List[str]] = Field(None, description="多选择器回退链")
    page_key: Optional[str] = Field(None, description="SelectorMapper 页面标识")
    element_key: Optional[str] = Field(None, description="元素标识")
    force: Optional[bool] = Field(None, description="强制点击")
    wait_after: Optional[int] = Field(None, description="自定义操作后延时(ms)")
    locator_type: Optional[str] = Field(None, description="定位类型: css/xpath/text/id/name")
    locator_value: Optional[str] = Field(None, description="定位值")
    semantic: Optional[Dict[str, Any]] = Field(
        None,
        description="语义目标（语义化用例执行）：{type, text, contains, scope, role, nth, container, save_as}",
    )
    verify: Optional[Dict[str, Any]] = Field(
        None,
        description="状态验证（防假通过）：{type, selector, text, timeout, must}",
    )
    run_on: Optional[str] = Field(
        None,
        description="执行时机: always/finally/on_failure/on_success（缺省 always）",
    )


class TestCaseBase(BaseModel):
    """
    用例基础模式

    包含用例的公共字段。
    """

    name: str = Field(..., min_length=1, max_length=200, description="用例名称")
    description: str = Field(default="", max_length=1000, description="用例描述")
    priority: str = Field(default="medium", description="优先级: high/medium/low")
    tags: List[str] = Field(default_factory=list, description="标签列表")
    steps: Union[List[StepDefinition], Dict[str, Any]] = Field(
        default_factory=list,
        description="测试步骤（UI用例为步骤列表，安全测试用例为配置字典）"
    )
    config: Dict[str, Any] = Field(
        default_factory=lambda: {"timeout": 30000, "retry": 0, "screenshot": True},
        description="执行配置"
    )
    case_type: str = Field(default="ui", description="用例类型: ui/api/security/deploy")
    parameters: Dict[str, Any] = Field(
        default_factory=dict,
        description="用例参数（上下文变量初始值，支持 {__CTX:key__} 引用）"
    )


class TestCaseCreate(TestCaseBase):
    """
    创建用例请求模式

    用于接收创建用例的请求数据。

    Attributes:
        module_id (str): 所属模块ID

    Example:
        >>> case_data = TestCaseCreate(
        ...     module_id="module_001",
        ...     name="正常登录测试",
        ...     steps=[
        ...         StepDefinition(order=1, name="打开登录页面", action="navigate", params={"url": "/login"}),
        ...         StepDefinition(order=2, name="输入用户名", action="fill", params={"selector": "#username"})
        ...     ]
        ... )
    """

    module_id: str = Field(..., description="所属模块ID")


class TestCaseUpdate(BaseModel):
    """
    更新用例请求模式

    用于接收更新用例的请求数据，所有字段可选。
    """

    name: Optional[str] = Field(None, min_length=1, max_length=200, description="用例名称")
    module_id: Optional[str] = Field(None, description="所属模块ID")
    description: Optional[str] = Field(None, max_length=1000, description="用例描述")
    status: Optional[str] = Field(None, description="状态: active/disabled")
    priority: Optional[str] = Field(None, description="优先级")
    tags: Optional[List[str]] = Field(None, description="标签列表")
    steps: Optional[Union[List[StepDefinition], Dict[str, Any]]] = Field(
        None, description="测试步骤（UI用例为步骤列表，安全测试用例为配置字典）"
    )
    config: Optional[Dict[str, Any]] = Field(None, description="执行配置")
    case_type: Optional[str] = Field(None, description="用例类型: ui/api/security/deploy")
    parameters: Optional[Dict[str, Any]] = Field(None, description="用例参数（上下文变量初始值）")


class TestCaseResponse(TestCaseBase):
    """
    用例响应模式

    用于返回用例数据给客户端。
    序列化时自动转换为 camelCase 以匹配前端 TypeScript 类型。

    Attributes:
        id (str): 用例ID
        module_id (str): 所属模块ID
        status (str): 状态
        created_at (datetime): 创建时间
        updated_at (datetime): 更新时间
    """

    model_config = ConfigDict(
        from_attributes=True,
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: str = Field(..., description="用例ID")
    module_id: str = Field(..., description="所属模块ID")
    status: str = Field(default="active", description="状态")
    created_at: Optional[datetime] = Field(None, description="创建时间")
    updated_at: Optional[datetime] = Field(None, description="更新时间")


class TestCaseListResponse(BaseModel):
    """
    用例列表响应模式

    用于返回分页的用例列表。
    """

    model_config = ConfigDict(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    total: int = Field(..., description="总数")
    items: List[TestCaseResponse] = Field(default_factory=list, description="用例列表")


class TestCaseImport(BaseModel):
    """
    用例导入模式

    用于从文件导入用例。

    Attributes:
        name (str): 用例名称
        module_name (str): 目标模块名称（按名称匹配）
        description (str): 用例描述
        priority (str): 优先级
        tags (List[str]): 标签列表
        steps (List[StepDefinition]): 测试步骤
        config (dict): 执行配置
    """

    name: str = Field(..., min_length=1, max_length=200, description="用例名称")
    module_name: str = Field(..., description="目标模块名称")
    description: str = Field(default="", description="用例描述")
    priority: str = Field(default="medium", description="优先级")
    tags: List[str] = Field(default_factory=list, description="标签列表")
    steps: List[StepDefinition] = Field(default_factory=list, description="测试步骤")
    config: Dict[str, Any] = Field(
        default_factory=lambda: {"timeout": 30000, "retry": 0, "screenshot": True},
        description="执行配置"
    )
    case_type: str = Field(default="ui", description="用例类型: ui/api/security/deploy")
    parameters: Dict[str, Any] = Field(
        default_factory=dict,
        description="用例参数（上下文变量初始值，支持 {__CTX:key__} 引用）"
    )