# =============================================================================
# 企微IT智能服务台 — 火绒集成数据模型
# =============================================================================
# 说明：火绒API请求/响应的 Pydantic 数据模型
# 包含：终端信息、漏洞信息、病毒事件、任务下发等
# =============================================================================

from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field, model_validator


# ==========================================================================
# 通用响应模型
# ==========================================================================

class HuorongApiResponse(BaseModel):
    """火绒API统一响应模型。

    火绒所有API返回格式一致（官方API文档 v1）：
    成功时: { "errno": 0, "errmsg": "", "data": { ... } }
    失败时: { "errno": 1, "errmsg": "Authentication failed" }

    官方错误码定义：
    - errno=0: 成功
    - errno=1: 认证失败
    - errno=2: 参数错误
    - errno=3: 服务器内部错误
    - errno=4: API未授权

    注意：火绒API始终使用 errno（不是 errcode）。
    使用 model_validator 在验证前将 errno 归一化为 errcode，
    保持内部代码统一使用 errcode 字段。

    Attributes:
        errcode: 错误码，0表示成功（从 errno 归一化而来）
        errmsg: 错误描述（成功时为空字符串）
        data: 业务数据（成功时非None）
    """

    @model_validator(mode='before')
    @classmethod
    def normalize_error_fields(cls, data: Any) -> Any:
        """将火绒API返回的 errno 字段归一化为 errcode。

        火绒API在认证失败等错误场景下返回 errno 而非 errcode，
        此验证器在 Pydantic 字段校验前将 errno 转换为 errcode，
        统一后续处理逻辑。

        Args:
            data: 原始输入数据（通常为dict）

        Returns:
            归一化后的数据
        """
        if isinstance(data, dict) and 'errno' in data and 'errcode' not in data:
            data['errcode'] = data.pop('errno')
        return data

    errcode: int = Field(..., description="错误码，0=成功")
    errmsg: str = Field(default="ok", description="错误描述")
    data: Optional[Any] = Field(default=None, description="业务数据")


# ==========================================================================
# 终端基本信息 — /api/clnts/_list 返回
# ==========================================================================

class TerminalBasicInfo(BaseModel):
    """终端基本信息（_list 接口返回的每条记录）。

    字段名严格按照火绒API文档实际返回值定义。
    注意：API返回的字段名与之前猜测不同，已根据官方文档修正。

    Attributes:
        id: 内部数据库ID
        client_id: 终端唯一ID（40位十六进制字符串，用于所有任务下发）
        client_name: 客户端名称
        computer_name: 计算机名
        local_ip: 本地IP
        connect_ip: 连接IP（客户端连接控制中心使用的IP）
        mac: MAC地址
        group_id: 分组ID
        os_version: 操作系统版本
        version: 火绒客户端版本
        definitions: 病毒库更新时间
        is_online: 在线状态
        last_connect_time: 最后连接时间（Unix时间戳）
        last_seen_time: 最后可见时间（Unix时间戳）
        first_appear_time: 首次出现时间（Unix时间戳）
    """
    id: Optional[int] = Field(default=None, description="内部数据库ID")
    client_id: str = Field(..., description="终端唯一ID")
    client_name: str = Field(default="", description="客户端名称")
    computer_name: str = Field(default="", description="计算机名")
    local_ip: str = Field(default="", description="本地IP")
    connect_ip: str = Field(default="", description="连接IP")
    mac: str = Field(default="", description="MAC地址")
    group_id: Optional[Any] = Field(default=None, description="分组ID（int或str）")
    os_version: str = Field(default="", description="操作系统版本")
    version: str = Field(default="", description="火绒客户端版本")
    definitions: str = Field(default="", description="病毒库更新时间")
    is_online: bool = Field(default=False, description="在线状态")
    last_connect_time: Optional[int] = Field(default=None, description="最后连接时间")
    last_seen_time: Optional[int] = Field(default=None, description="最后可见时间")
    first_appear_time: Optional[int] = Field(default=None, description="首次出现时间")


class TerminalListRequest(BaseModel):
    """终端列表查询请求。

    Attributes:
        group_id: 分组ID（可选，不传则查全部分组）
        page: 页码（从1开始）
        per_page: 每页条数
    """
    group_id: Optional[str] = Field(default=None, description="分组ID")
    page: int = Field(default=1, ge=1, description="页码")
    per_page: int = Field(default=20, ge=1, le=100, description="每页条数")


# ==========================================================================
# 终端详细信息v2 — /api/clnts/_info2 返回
# ==========================================================================

class HardwareInfo(BaseModel):
    """终端硬件信息。

    Attributes:
        cpu: CPU信息
        memory: 内存信息
        disk: 磁盘信息
        motherboard: 主板信息
        network_card: 网卡信息
    """
    cpu: str = Field(default="", description="CPU信息")
    memory: str = Field(default="", description="内存信息")
    disk: str = Field(default="", description="磁盘信息")
    motherboard: str = Field(default="", description="主板信息")
    network_card: str = Field(default="", description="网卡信息")


class SoftwareInfo(BaseModel):
    """已安装软件条目。

    Attributes:
        name: 软件名称
        version: 版本号
        publisher: 发布者
    """
    name: str = Field(default="", description="软件名称")
    version: str = Field(default="", description="版本号")
    publisher: str = Field(default="", description="发布者")


class AssetInfo(BaseModel):
    """资产信息。

    Attributes:
        asset_tag: 资产标签
        serial_number: 序列号
    """
    asset_tag: str = Field(default="", description="资产标签")
    serial_number: str = Field(default="", description="序列号")


class NetworkConfig(BaseModel):
    """网络配置信息。

    Attributes:
        ip: IP地址
        gateway: 网关
        dns: DNS服务器
        adapter_info: 网卡适配器信息
    """
    ip: str = Field(default="", description="IP地址")
    gateway: str = Field(default="", description="网关")
    dns: str = Field(default="", description="DNS服务器")
    adapter_info: str = Field(default="", description="网卡适配器信息")


class TerminalDetailV2(BaseModel):
    """终端详细信息v2（_info2 接口返回）。

    通过 optional_fields 参数指定需要返回的信息块：
    - hardware: 硬件信息
    - software: 已安装软件
    - assets: 资产信息
    - netconf: 网络配置

    Attributes:
        client_id: 终端唯一ID
        computer_name: 计算机名
        hardware: 硬件信息（可选）
        software: 已安装软件列表（可选）
        assets: 资产信息（可选）
        netconf: 网络配置（可选）
    """
    client_id: str = Field(..., description="终端唯一ID")
    computer_name: str = Field(default="", description="计算机名")
    hardware: Optional[HardwareInfo] = Field(default=None, description="硬件信息")
    software: Optional[List[SoftwareInfo]] = Field(default=None, description="已安装软件")
    assets: Optional[AssetInfo] = Field(default=None, description="资产信息")
    netconf: Optional[NetworkConfig] = Field(default=None, description="网络配置")


class TerminalDetailRequest(BaseModel):
    """终端详细信息查询请求。

    Attributes:
        client_id: 终端唯一ID
        optional_fields: 需要返回的可选信息块列表
    """
    client_id: str = Field(..., description="终端唯一ID")
    optional_fields: List[str] = Field(
        default_factory=lambda: ["hardware", "software", "assets", "netconf"],
        description="可选信息块: hardware/software/assets/netconf",
    )


# ==========================================================================
# 漏洞信息 — /api/clnts/_leak 返回
# 说明：_leak 接口返回的是"存在高危漏洞未修复的终端列表"，
#       每条记录是终端信息（非漏洞详情），API不返回具体漏洞CVE列表。
#       外层还有 all_client（终端总数）和 risk_client（高危终端数）统计。
# ==========================================================================

class TerminalLeakInfo(BaseModel):
    """存在高危漏洞的终端信息（_leak 接口返回的每条记录）。

    注意：_leak 返回的是终端维度数据，不是漏洞维度。
    字段名严格按照火绒API文档实际返回值定义。
    与 _list 接口的字段名不同！

    Attributes:
        cid: 终端唯一ID（_leak 中叫 cid，_list 中叫 client_id）
        hostname: 计算机名（_leak 中叫 hostname，_list 中叫 computer_name）
        client_name: 终端名称
        group_name: 分组名称
        group_id: 分组ID
        ip_addr: 本地IP（_leak 中叫 ip_addr，_list 中叫 local_ip）
        call_ip: 连接IP（_leak 中叫 call_ip，_list 中叫 connect_ip）
        mac: MAC地址
        osver: 操作系统版本（_leak 中叫 osver，_list 中叫 os_version）
        os_type: 终端类型（如 Windows）
        prodver: 火绒客户端版本（_leak 中叫 prodver，_list 中叫 version）
        virdb: 病毒库版本（Unix时间戳，_leak 中叫 virdb，_list 中叫 definitions）
        stat: 在线状态码（1=离线, 2=在线, 3=异常，_list 中是 is_online 布尔值）
    """
    cid: str = Field(..., description="终端唯一ID")
    hostname: str = Field(default="", description="计算机名")
    client_name: str = Field(default="", description="终端名称")
    group_name: str = Field(default="", description="分组名称")
    group_id: Optional[Any] = Field(default=None, description="分组ID")
    ip_addr: str = Field(default="", description="本地IP")
    call_ip: str = Field(default="", description="连接IP")
    mac: str = Field(default="", description="MAC地址")
    osver: str = Field(default="", description="操作系统版本")
    os_type: str = Field(default="", description="终端类型")
    prodver: str = Field(default="", description="火绒客户端版本")
    virdb: Optional[Any] = Field(default=None, description="病毒库版本(Unix时间戳)")
    stat: int = Field(default=1, description="在线状态码: 1=离线 2=在线 3=异常")


# ==========================================================================
# 病毒事件 — /api/clnts/_virus_events 返回
# 说明：_virus_events 返回终端维度的病毒日志统计，
#       含总数(count)和4种处理结果(result)的明细。
#       请求需指定 type: 0=按client_id查, 1=按group_id查, 2=查全部
# ==========================================================================

class VirusHandleResult(BaseModel):
    """病毒事件处理结果统计。

    Attributes:
        success: 处理成功数
        fail: 处理失败数
        ignored: 暂不处理数
        trusted: 已信任数
    """
    success: int = Field(default=0, description="处理成功数")
    fail: int = Field(default=0, description="处理失败数")
    ignored: int = Field(default=0, description="暂不处理数")
    trusted: int = Field(default=0, description="已信任数")


class VirusEventStats(BaseModel):
    """终端病毒事件统计（_virus_events 接口返回的每条记录）。

    字段名严格按照火绒API文档实际返回值定义。
    与 _list 接口的字段名基本一致。

    Attributes:
        group_id: 分组ID
        client_id: 终端唯一ID
        client_name: 终端名称
        computer_name: 计算机名
        local_ip: 本地IP
        connect_ip: 连接IP
        mac: MAC地址
        count: 病毒日志总数
        result: 处理结果统计（success/fail/ignored/trusted）
    """
    group_id: Optional[Any] = Field(default=None, description="分组ID")
    client_id: str = Field(..., description="终端唯一ID")
    client_name: str = Field(default="", description="终端名称")
    computer_name: str = Field(default="", description="计算机名")
    local_ip: str = Field(default="", description="本地IP")
    connect_ip: str = Field(default="", description="连接IP")
    mac: str = Field(default="", description="MAC地址")
    count: int = Field(default=0, description="病毒日志总数")
    result: Optional[VirusHandleResult] = Field(default=None, description="处理结果统计")


# ==========================================================================
# 终端任务 — /api/task/_create
# ==========================================================================

class TaskCreateRequest(BaseModel):
    """终端任务创建请求。

    支持的任务类型：
    - quick_scan: 快速扫描
    - full_scan: 全盘扫描
    - custom_scan: 自定义扫描
    - netctrl: 终端隔离/解除
    - message: 发送通知

    Attributes:
        task_type: 任务类型
        client_ids: 目标终端ID列表
        net_isolation: 是否隔离（仅 netctrl 类型有效）
        message_content: 通知内容（仅 message 类型有效）
    """
    task_type: str = Field(..., description="任务类型: quick_scan/full_scan/custom_scan/netctrl/message")
    client_ids: List[str] = Field(..., min_length=1, description="目标终端ID列表")
    net_isolation: Optional[bool] = Field(default=None, description="是否隔离（仅netctrl类型）")
    message_content: Optional[str] = Field(default=None, description="通知内容（仅message类型）")


# ==========================================================================
# 终端安全画像（聚合模型，供前端直接使用）
# ==========================================================================

class TerminalSecurityProfile(BaseModel):
    """终端安全画像（聚合模型）。

    将终端基本信息+安全状态聚合成一个模型，供坐席端直接展示。

    Attributes:
        client_id: 终端唯一ID
        computer_name: 计算机名
        ip: 本地IP
        mac: MAC地址
        os_version: 操作系统版本
        is_online: 在线状态
        group_name: 分组名称
        hardware: 硬件概要
        high_risk_leaks: 高危漏洞数
        uncleaned_virus: 未处理病毒事件数
        security_score: 安全评分（0-100，综合漏洞+病毒+在线状态）
    """
    client_id: str = Field(..., description="终端唯一ID")
    computer_name: str = Field(default="", description="计算机名")
    ip: str = Field(default="", description="本地IP")
    mac: str = Field(default="", description="MAC地址")
    os_version: str = Field(default="", description="操作系统版本")
    is_online: bool = Field(default=False, description="在线状态")
    group_name: str = Field(default="", description="分组名称")
    hardware: Optional[HardwareInfo] = Field(default=None, description="硬件概要")
    high_risk_leaks: int = Field(default=0, description="高危漏洞数")
    uncleaned_virus: int = Field(default=0, description="未处理病毒事件数")
    security_score: int = Field(default=100, description="安全评分(0-100)")
