#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：performance.py
模块描述：性能测试数据库模型定义

作者：czj
创建日期：2026-08-12
最后修改：2026-08-12
"""

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

from app.database import Base


class PerformanceTask(Base):
    """
    性能测试任务数据库模型

    记录每次性能测试的整体信息，包括压测配置、结果统计等。

    Attributes:
        id (str): 任务唯一标识
        name (str): 任务名称
        description (str): 任务描述
        status (str): 状态 (pending/running/completed/failed/cancelled)

        # 目标配置
        target_url (str): 目标接口完整 URL
        method (str): HTTP 方法 (GET/POST/PUT/DELETE)
        headers (dict): 自定义请求头
        body (dict): 请求体
        body_template (dict): 请求体模板（支持变量替换）

        # 压测配置
        mode (str): 压测模式 (concurrent/qps/step)
        concurrency (int): 并发数（并发模式）
        qps_target (int): 目标 QPS（QPS 模式）
        duration (int): 压测时长（秒）
        ramp_up (int): 预热时间（秒）
        step_concurrency (list): 阶梯并发配置，如 [10, 50, 100, 200]
        step_duration (int): 每阶梯持续时间（秒）

        # 认证配置
        auth_required (bool): 是否需要登录 Token
        account_key (str): 使用的账号 (superadmin/admin/user)

        # 断言配置
        assertions (list): 断言规则列表

        # 结果统计
        total_requests (int): 总请求数
        success_count (int): 成功数
        fail_count (int): 失败数
        min_response_time (float): 最小响应时间 (ms)
        max_response_time (float): 最大响应时间 (ms)
        avg_response_time (float): 平均响应时间 (ms)
        p50_response_time (float): P50 响应时间 (ms)
        p90_response_time (float): P90 响应时间 (ms)
        p99_response_time (float): P99 响应时间 (ms)
        actual_tps (float): 实际吞吐量
        error_rate (float): 错误率

        # 时间
        start_time (datetime): 开始时间
        end_time (datetime): 结束时间
        duration_actual (float): 实际耗时（秒）
        created_at (datetime): 创建时间
        updated_at (datetime): 更新时间
    """

    __tablename__ = "performance_tasks"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, comment="任务ID")
    name: Mapped[str] = mapped_column(String(200), default="", comment="任务名称")
    description: Mapped[str] = mapped_column(Text, default="", comment="任务描述")
    status: Mapped[str] = mapped_column(
        String(20),
        default="pending",
        comment="状态: pending/running/completed/failed/cancelled"
    )
    error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="错误信息")

    # 目标配置
    target_url: Mapped[str] = mapped_column(Text, default="", comment="目标接口完整URL")
    method: Mapped[str] = mapped_column(String(10), default="GET", comment="HTTP方法")
    headers: Mapped[dict] = mapped_column(JSON, default=dict, comment="自定义请求头")
    body: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="请求体")
    body_template: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="请求体模板")

    # 压测配置
    mode: Mapped[str] = mapped_column(
        String(20),
        default="concurrent",
        comment="压测模式: concurrent/qps/step"
    )
    concurrency: Mapped[int] = mapped_column(Integer, default=10, comment="并发数")
    qps_target: Mapped[int] = mapped_column(Integer, default=100, comment="目标QPS")
    duration: Mapped[int] = mapped_column(Integer, default=60, comment="压测时长(秒)")
    ramp_up: Mapped[int] = mapped_column(Integer, default=10, comment="预热时间(秒)")
    step_concurrency: Mapped[list] = mapped_column(JSON, default=list, comment="阶梯并发配置")
    step_duration: Mapped[int] = mapped_column(Integer, default=60, comment="每阶梯时长(秒)")

    # 认证配置
    auth_required: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否需要登录Token")
    account_key: Mapped[str] = mapped_column(String(50), default="superadmin", comment="使用的账号")
    login_username: Mapped[Optional[str]] = mapped_column(
        String(200), nullable=True, default=None, comment="自定义登录用户名"
    )
    login_password: Mapped[Optional[str]] = mapped_column(
        String(200), nullable=True, default=None, comment="自定义登录密码"
    )

    # 签名配置
    sign_request: Mapped[bool] = mapped_column(Boolean, default=False, comment="请求是否需要签名(X-RANDOM/X-TIMESTAMP/X-SIGN)")

    # 关联接口预设（可选，仅记录来源，不参与执行）
    preset_id: Mapped[Optional[str]] = mapped_column(
        String(64), nullable=True, default=None, comment="关联的接口预设ID"
    )

    # 断言配置
    assertions: Mapped[list] = mapped_column(JSON, default=list, comment="断言规则列表")

    # 响应捕获规则
    capture_rules: Mapped[Optional[list]] = mapped_column(JSON, nullable=True, comment="响应捕获规则列表")

    # 结果统计
    total_requests: Mapped[int] = mapped_column(Integer, default=0, comment="总请求数")
    success_count: Mapped[int] = mapped_column(Integer, default=0, comment="成功数")
    fail_count: Mapped[int] = mapped_column(Integer, default=0, comment="失败数")
    min_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最小响应时间(ms)")
    max_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最大响应时间(ms)")
    avg_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="平均响应时间(ms)")
    p50_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P50响应时间(ms)")
    p90_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P90响应时间(ms)")
    p99_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P99响应时间(ms)")
    actual_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="实际吞吐量")
    error_rate: Mapped[float] = mapped_column(Float, default=0.0, comment="错误率(%)")

    # 时间
    start_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="开始时间")
    end_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="结束时间")
    duration_actual: Mapped[float] = mapped_column(Float, default=0.0, comment="实际耗时(秒)")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间"
    )

    # 关联快照（一对多）
    snapshots: Mapped[list["PerformanceSnapshot"]] = relationship(
        "PerformanceSnapshot",
        back_populates="task",
        cascade="all, delete-orphan"
    )

    def __repr__(self) -> str:
        """字符串表示"""
        return f"<PerformanceTask(id={self.id}, name={self.name}, status={self.status})>"

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

        Returns:
            dict: 任务数据字典
        """
        return {
            "id": self.id,
            "name": self.name,
            "description": self.description,
            "status": self.status,
            "error_message": self.error_message,
            "target_url": self.target_url,
            "method": self.method,
            "headers": self.headers or {},
            "body": self.body,
            "body_template": self.body_template,
            "mode": self.mode,
            "concurrency": self.concurrency,
            "qps_target": self.qps_target,
            "duration": self.duration,
            "ramp_up": self.ramp_up,
            "step_concurrency": self.step_concurrency or [],
            "step_duration": self.step_duration,
            "auth_required": self.auth_required,
            "account_key": self.account_key,
            "login_username": self.login_username,
            "login_password": self.login_password,
            "sign_request": self.sign_request,
            "preset_id": self.preset_id,
            "assertions": self.assertions or [],
            "capture_rules": self.capture_rules or [],
            "total_requests": self.total_requests,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "min_response_time": self.min_response_time,
            "max_response_time": self.max_response_time,
            "avg_response_time": self.avg_response_time,
            "p50_response_time": self.p50_response_time,
            "p90_response_time": self.p90_response_time,
            "p99_response_time": self.p99_response_time,
            "actual_tps": self.actual_tps,
            "error_rate": self.error_rate,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "duration_actual": self.duration_actual,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "updated_at": self.updated_at.isoformat() if self.updated_at else None,
        }


class PerformanceSnapshot(Base):
    """
    性能测试快照数据库模型

    每秒采集一次性能指标，用于实时监控和历史回放分析。

    Attributes:
        id (int): 自增主键
        task_id (str): 关联任务ID
        timestamp (float): 采集时间戳
        elapsed (float): 距开始时间（秒）
        concurrent_users (int): 当前并发数
        tps (float): 当前吞吐量
        avg_response_time (float): 平均响应时间 (ms)
        min_response_time (float): 最小响应时间 (ms)
        max_response_time (float): 最大响应时间 (ms)
        p50_response_time (float): P50 响应时间 (ms)
        p90_response_time (float): P90 响应时间 (ms)
        p99_response_time (float): P99 响应时间 (ms)
        success_count (int): 本周期成功请求数
        fail_count (int): 本周期失败请求数
        status_2xx (int): 2xx 状态码计数
        status_3xx (int): 3xx 状态码计数
        status_4xx (int): 4xx 状态码计数
        status_5xx (int): 5xx 状态码计数
    """

    __tablename__ = "performance_snapshots"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="快照ID")
    task_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("performance_tasks.id", ondelete="CASCADE"), comment="关联任务ID"
    )
    timestamp: Mapped[float] = mapped_column(Float, comment="采集时间戳")
    elapsed: Mapped[float] = mapped_column(Float, comment="距开始时间(秒)")
    concurrent_users: Mapped[int] = mapped_column(Integer, default=0, comment="当前并发数")
    tps: Mapped[float] = mapped_column(Float, default=0.0, comment="当前吞吐量")
    avg_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="平均响应时间(ms)")
    min_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最小响应时间(ms)")
    max_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最大响应时间(ms)")
    p50_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P50响应时间(ms)")
    p90_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P90响应时间(ms)")
    p99_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P99响应时间(ms)")
    success_count: Mapped[int] = mapped_column(Integer, default=0, comment="本周期成功数")
    fail_count: Mapped[int] = mapped_column(Integer, default=0, comment="本周期失败数")
    status_2xx: Mapped[int] = mapped_column(Integer, default=0, comment="2xx计数")
    status_3xx: Mapped[int] = mapped_column(Integer, default=0, comment="3xx计数")
    status_4xx: Mapped[int] = mapped_column(Integer, default=0, comment="4xx计数")
    status_5xx: Mapped[int] = mapped_column(Integer, default=0, comment="5xx计数")

    # 关联任务
    task: Mapped["PerformanceTask"] = relationship("PerformanceTask", back_populates="snapshots")

    def __repr__(self) -> str:
        """字符串表示"""
        return f"<PerformanceSnapshot(id={self.id}, task_id={self.task_id}, elapsed={self.elapsed}s)>"

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

        Returns:
            dict: 快照数据字典
        """
        return {
            "id": self.id,
            "task_id": self.task_id,
            "timestamp": self.timestamp,
            "elapsed": self.elapsed,
            "concurrent_users": self.concurrent_users,
            "tps": self.tps,
            "avg_response_time": self.avg_response_time,
            "min_response_time": self.min_response_time,
            "max_response_time": self.max_response_time,
            "p50_response_time": self.p50_response_time,
            "p90_response_time": self.p90_response_time,
            "p99_response_time": self.p99_response_time,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "status_2xx": self.status_2xx,
            "status_3xx": self.status_3xx,
            "status_4xx": self.status_4xx,
            "status_5xx": self.status_5xx,
        }