#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：vulnerability_result.py
模块描述：漏洞测试结果数据库模型定义

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

from datetime import datetime
from typing import Optional
from sqlalchemy import String, Text, JSON, DateTime, Boolean, Float, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base


class VulnerabilityResult(Base):
    """
    漏洞测试结果数据库模型

    存储安全测试用例的执行结果，包括漏洞信息、请求响应详情等。

    Attributes:
        id (str): 结果唯一标识
        execution_id (str): 关联的执行记录ID
        case_id (str): 关联的测试用例ID
        test_id (str): OWASP测试编号（如 2.1.1）
        name (str): 测试用例名称
        level (str): 风险等级（critical/high/medium/low/info）
        description (str): 漏洞描述
        request_info (str): 请求信息（用于复现）
        response_info (str): 响应信息（用于复现）
        is_vulnerable (bool): 是否存在漏洞
        fix_suggestion (str): 修复建议
        duration (float): 执行耗时（秒）
        status (str): 执行状态（passed/failed/skipped/error）
        error_message (str): 错误信息
        metadata (dict): 额外元数据（回归测试标记、红线标记等）
        created_at (datetime): 创建时间
    """

    __tablename__ = "vulnerability_results"

    # 风险等级常量
    LEVEL_CRITICAL = "critical"
    LEVEL_HIGH = "high"
    LEVEL_MEDIUM = "medium"
    LEVEL_LOW = "low"
    LEVEL_INFO = "info"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, comment="结果ID")
    execution_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("executions.id", ondelete="CASCADE"),
        nullable=False, comment="关联执行ID"
    )
    case_id: Mapped[str] = mapped_column(String(64), default="", comment="关联用例ID")

    # 漏洞信息
    test_id: Mapped[str] = mapped_column(String(20), default="", comment="OWASP测试编号(如2.1.1)")
    name: Mapped[str] = mapped_column(String(200), nullable=False, comment="测试用例名称")
    level: Mapped[str] = mapped_column(
        String(20), default="info",
        comment="风险等级: critical/high/medium/low/info"
    )
    description: Mapped[str] = mapped_column(Text, default="", comment="漏洞描述")

    # 请求/响应信息
    request_info: Mapped[str] = mapped_column(Text, default="", comment="请求信息(用于复现)")
    response_info: Mapped[str] = mapped_column(Text, default="", comment="响应信息(用于复现)")

    # 结果
    is_vulnerable: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否存在漏洞")
    fix_suggestion: Mapped[str] = mapped_column(Text, default="", comment="修复建议")

    # 执行信息
    duration: Mapped[float] = mapped_column(Float, default=0.0, comment="执行耗时(秒)")
    status: Mapped[str] = mapped_column(
        String(20), default="pending",
        comment="执行状态: pending/running/passed/failed/skipped/error"
    )
    error_message: Mapped[Optional[str]] = mapped_column(
        Text, nullable=True, default=None, comment="错误信息"
    )

    # 元数据（注意：metadata 是 SQLAlchemy 保留字段名）
    # 格式: {"is_regression": true, "vuln_source": "长安深蓝汽车", "is_huawei_redline": false, ...}
    extra_data: Mapped[dict] = mapped_column(JSON, default=dict, comment="额外元数据")

    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )

    # 关联执行记录（多对一）
    execution: Mapped["Execution"] = relationship(
        "Execution", back_populates="vulnerability_results"
    )

    def __repr__(self) -> str:
        """字符串表示"""
        vuln_mark = "🔴" if self.is_vulnerable else "✅"
        return f"<VulnerabilityResult(id={self.id}, test_id={self.test_id}, {vuln_mark} {self.level})>"

    def to_dict(self) -> dict:
        """
        转换为字典

        Returns:
            dict: 结果数据字典
        """
        return {
            "id": self.id,
            "execution_id": self.execution_id,
            "case_id": self.case_id,
            "test_id": self.test_id,
            "name": self.name,
            "level": self.level,
            "description": self.description,
            "request_info": self.request_info,
            "response_info": self.response_info,
            "is_vulnerable": self.is_vulnerable,
            "fix_suggestion": self.fix_suggestion,
            "duration": self.duration,
            "status": self.status,
            "error_message": self.error_message,
            "extra_data": self.extra_data or {},
            "created_at": self.created_at.isoformat() if self.created_at else None,
        }

    @staticmethod
    def level_order(level: str) -> int:
        """
        获取风险等级排序值（用于按等级排序）

        Args:
            level: 风险等级

        Returns:
            int: 排序值，数值越小风险越高
        """
        order_map = {
            "critical": 0,
            "high": 1,
            "medium": 2,
            "low": 3,
            "info": 4,
        }
        return order_map.get(level, 99)

    @property
    def level_emoji(self) -> str:
        """风险等级对应的emoji标记"""
        emoji_map = {
            "critical": "🔴🔴🔴",
            "high": "🔴",
            "medium": "🟠",
            "low": "🟡",
            "info": "🔵",
        }
        return emoji_map.get(self.level, "⚪")
