#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：performance.py
模块描述：性能测试 API 路由
  - Phase 1: CRUD 占位（已完成）
  - Phase 3: 执行/停止/快照/报告/WebSocket

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

import asyncio
import json
import logging
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query, WebSocket, WebSocketDisconnect
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import get_db, async_session_maker
from app.schemas.performance import (
    PerformanceTaskCreate,
    PerformanceTaskUpdate,
    PerformanceTaskResponse,
    PerformanceTaskListItem,
    PerformanceTaskListResponse,
    PerformanceRunRequest,
    PerformanceRunResponse,
    PerformanceSnapshotResponse,
    PerformanceSnapshotListResponse,
    PerformanceReportResponse,
    PerformanceReportSummary,
    PerformanceReportMetrics,
    PerformanceExecutionListItem,
    PerformanceExecutionListResponse,
    PerformanceExecutionDetailResponse,
    PerformanceExecutionReportResponse,
    CurlParseRequest,
    CurlParseResponse,
    PerformanceProjectCreate,
    PerformanceProjectUpdate,
    PerformanceProjectResponse,
    PerformanceProjectListResponse,
    PerformanceProjectStatsResponse,
    BatchRunRequest,
    BatchRunResponse,
    BatchTaskItem,
    BatchDetailResponse,
    ProjectReportResponse,
    ProjectReportListResponse,
    AiAnalysisResponse,
    AiAnalysisCompareRequest,
)
from app.services.performance_service import PerformanceService
from app.services.performance_ai_service import PerformanceAiService
from app.utils.curl_parser import parse_curl
from app.websocket.manager import manager

logger = logging.getLogger(__name__)

router = APIRouter()


# ==================== 依赖注入 ====================

async def get_perf_service(db: AsyncSession = Depends(get_db)) -> PerformanceService:
    """获取 PerformanceService 实例"""
    return PerformanceService(db)


# ==================== Phase 1: CRUD ====================


@router.get("/tasks", response_model=PerformanceTaskListResponse)
async def list_tasks(
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(20, ge=1, le=100, description="每页数量"),
    status: Optional[str] = Query(None, description="按状态过滤"),
    project_id: Optional[str] = Query(None, description="按项目过滤；传 null 查未归属任务，不传返回全部"),
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取性能测试任务列表（分页）

    - 不传 project_id：返回全部任务（向后兼容）
    - project_id=null / empty：返回未归属任何项目的任务
    - project_id=xxx：返回指定项目下的任务
    """
    _project_id = None
    if project_id == "null" or project_id == "":
        _project_id = ""
    else:
        _project_id = project_id

    items, total = await service.list_tasks(
        page=page, page_size=page_size, status=status, project_id=_project_id
    )
    return PerformanceTaskListResponse(
        total=total,
        items=[PerformanceTaskListItem(**t.to_dict()) for t in items],
    )


@router.post("/tasks", response_model=PerformanceTaskResponse, status_code=201)
async def create_task(
    data: PerformanceTaskCreate,
    service: PerformanceService = Depends(get_perf_service),
):
    """创建性能测试任务"""
    task = await service.create_task(data)
    return PerformanceTaskResponse(**task.to_dict())


@router.get("/tasks/{task_id}", response_model=PerformanceTaskResponse)
async def get_task(
    task_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """获取性能测试任务详情"""
    task = await service.get_task(task_id)
    if not task:
        raise HTTPException(status_code=404, detail="任务不存在")
    return PerformanceTaskResponse(**task.to_dict())


@router.put("/tasks/{task_id}", response_model=PerformanceTaskResponse)
async def update_task(
    task_id: str,
    data: PerformanceTaskUpdate,
    service: PerformanceService = Depends(get_perf_service),
):
    """更新性能测试任务"""
    try:
        task = await service.update_task(task_id, data.model_dump(exclude_unset=True))
        if not task:
            raise HTTPException(status_code=404, detail="任务不存在")
        return PerformanceTaskResponse(**task.to_dict())
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@router.delete("/tasks/{task_id}", status_code=204)
async def delete_task(
    task_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """删除性能测试任务"""
    try:
        deleted = await service.delete_task(task_id)
        if not deleted:
            raise HTTPException(status_code=404, detail="任务不存在")
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


# ==================== curl 解析 ====================


@router.post("/parse-curl", response_model=CurlParseResponse)
async def parse_curl_endpoint(data: CurlParseRequest):
    """
    解析 curl 命令，返回结构化压测配置

    支持 -X/--request、-H/--header、-d/--data* 及目标 URL；
    自动识别并剥离 Authorization Bearer 与签名头（X-RANDOM/X-TIMESTAMP/X-SIGN）；
    Cookie 头保留在 headers 中。
    不支持的参数（-F/-u/--data-urlencode）通过 errors 列表友好提示，不阻断解析。
    """
    try:
        result = parse_curl(data.curl_command)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    return CurlParseResponse(
        method=result.method,
        target_url=result.target_url,
        headers=result.headers,
        body=result.body,
        auth_required=result.auth_required,
        sign_request=result.sign_request,
        cookie=result.cookie,
        errors=result.errors,
    )


# ==================== Phase 3: 执行/停止 ====================


@router.post("/tasks/{task_id}/run", response_model=PerformanceRunResponse)
async def run_task(
    task_id: str,
    data: PerformanceRunRequest = None,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    执行性能测试任务（非阻塞，立即返回）

    创建执行记录后立即返回 execution_id，任务在后台异步执行；
    前端可凭 task_id + execution_id 连接 WebSocket 实时监控，执行完成后自动跳转报告页。
    """
    if data is None:
        data = PerformanceRunRequest()

    try:
        result = await service.run_task_async(
            task_id,
            config_override=data.config_override,
        )
        return PerformanceRunResponse(
            message=result.get("message", "任务已开始执行"),
            task_id=task_id,
            execution_id=result.get("execution_id"),
            status=result.get("status", "running"),
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@router.post("/tasks/{task_id}/stop", response_model=PerformanceRunResponse)
async def stop_task(
    task_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    停止正在运行的性能测试任务

    发送停止信号给执行引擎，引擎在下次分片检查时退出。
    """
    stopped = await service.stop_task(task_id)
    if not stopped:
        # 检查任务是否存在
        task = await service.get_task(task_id)
        if not task:
            raise HTTPException(status_code=404, detail="任务不存在")
        # 存在但不在运行中，返回提示
        return PerformanceRunResponse(
            message="任务当前不在运行状态",
            task_id=task_id,
            status=task.status,
        )

    return PerformanceRunResponse(
        message="已发送停止信号",
        task_id=task_id,
        status="cancelled",
    )


# ==================== Phase 3: 快照/报告 ====================


@router.get("/tasks/{task_id}/snapshots", response_model=PerformanceSnapshotListResponse)
async def get_snapshots(
    task_id: str,
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(500, ge=1, le=5000, description="每页数量"),
    service: PerformanceService = Depends(get_perf_service),
):
    """获取性能测试快照数据（分页，按时间升序）"""
    snapshots, total = await service.get_snapshots(
        task_id, page=page, page_size=page_size
    )
    return PerformanceSnapshotListResponse(
        total=total,
        items=[PerformanceSnapshotResponse(**s.to_dict()) for s in snapshots],
    )


@router.get("/tasks/{task_id}/report", response_model=PerformanceReportResponse)
async def get_report(
    task_id: str,
    execution_id: Optional[str] = Query(None, description="指定执行记录ID，不传则取最新"),
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取性能测试报告

    返回完整报告数据，包含执行摘要、指标统计和所有快照。
    支持通过 execution_id 查询历史执行记录的报告。
    """
    report = await service.get_report(task_id, execution_id=execution_id)
    if not report:
        raise HTTPException(status_code=404, detail="报告不存在")

    return PerformanceReportResponse(
        summary=PerformanceReportSummary(**report["summary"]),
        metrics=PerformanceReportMetrics(**report["metrics"]),
        snapshots=[PerformanceSnapshotResponse(**s) for s in report["snapshots"]],
        resource_summary=report.get("resource_summary"),
        target_resource_summary=report.get("target_resource_summary"),
        transaction_summary=report.get("transaction_summary"),
        api_summary=report.get("api_summary"),
    )


# ==================== 执行历史 ====================


@router.get("/executions", response_model=PerformanceExecutionListResponse)
async def list_executions(
    task_id: Optional[str] = Query(None, description="按任务筛选"),
    project_id: Optional[str] = Query(None, description="按项目筛选"),
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(20, ge=1, le=100, description="每页数量"),
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取性能测试执行历史列表（分页，按开始时间倒序）

    支持按 task_id 和 project_id 筛选，前端用于展示执行历史列表。
    """
    result = await service.get_executions(
        task_id=task_id,
        project_id=project_id,
        page=page,
        page_size=page_size,
    )
    return PerformanceExecutionListResponse(
        total=result["total"],
        items=[PerformanceExecutionListItem(**item) for item in result["items"]],
    )


@router.get("/executions/{execution_id}", response_model=PerformanceExecutionDetailResponse)
async def get_execution_detail(
    execution_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取执行记录详情
    """
    result = await service.get_execution_report(execution_id)
    if not result:
        raise HTTPException(status_code=404, detail="执行记录不存在")

    # 尝试获取完整执行记录对象
    from app.models.performance import PerformanceExecution
    from sqlalchemy import select
    query = select(PerformanceExecution).where(PerformanceExecution.id == execution_id)
    row = (await service.db.execute(query)).scalar_one_or_none()
    if not row:
        raise HTTPException(status_code=404, detail="执行记录不存在")

    return PerformanceExecutionDetailResponse(
        id=row.id,
        task_id=row.task_id,
        task_name=row.task_name,
        project_id=row.project_id,
        status=row.status,
        error_message=row.error_message,
        start_time=row.start_time.isoformat() if row.start_time else None,
        end_time=row.end_time.isoformat() if row.end_time else None,
        duration_actual=row.duration_actual or 0.0,
        total_requests=row.total_requests or 0,
        success_count=row.success_count or 0,
        fail_count=row.fail_count or 0,
        error_rate=row.error_rate or 0.0,
        actual_tps=row.actual_tps or 0.0,
        peak_tps=row.peak_tps or 0.0,
        min_response_time=row.min_response_time or 0.0,
        max_response_time=row.max_response_time or 0.0,
        avg_response_time=row.avg_response_time or 0.0,
        p50_response_time=row.p50_response_time or 0.0,
        p90_response_time=row.p90_response_time or 0.0,
        p95_response_time=row.p95_response_time or 0.0,
        p99_response_time=row.p99_response_time or 0.0,
        std_dev=row.std_dev or 0.0,
        latency_avg=row.latency_avg or 0.0,
        latency_min=row.latency_min or 0.0,
        latency_max=row.latency_max or 0.0,
        connect_time_avg=row.connect_time_avg or 0.0,
        connect_time_max=row.connect_time_max or 0.0,
        apdex=row.apdex or 0.0,
        total_sent_bytes=row.total_sent_bytes or 0,
        total_received_bytes=row.total_received_bytes or 0,
        resource_summary=row.resource_summary,
        target_resource_summary=row.target_resource_summary,
        transaction_summary=row.transaction_summary,
        api_summary=row.api_summary,
        snapshot_count=row.snapshot_count or 0,
        triggered_by=row.triggered_by or "manual",
        batch_id=row.batch_id,
        created_at=row.created_at.isoformat() if row.created_at else None,
    )


@router.get("/executions/{execution_id}/report", response_model=PerformanceReportResponse)
async def get_execution_report(
    execution_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取指定执行记录的报告

    效果同 GET /tasks/{task_id}/report?execution_id=xxx，但路径更直接。
    """
    report = await service.get_execution_report(execution_id)
    if not report:
        raise HTTPException(status_code=404, detail="执行报告不存在")

    return PerformanceReportResponse(
        summary=PerformanceReportSummary(**report["summary"]),
        metrics=PerformanceReportMetrics(**report["metrics"]),
        snapshots=[PerformanceSnapshotResponse(**s) for s in report["snapshots"]],
        resource_summary=report.get("resource_summary"),
        target_resource_summary=report.get("target_resource_summary"),
        transaction_summary=report.get("transaction_summary"),
        api_summary=report.get("api_summary"),
    )


@router.get("/executions/{execution_id}/snapshots", response_model=PerformanceSnapshotListResponse)
async def get_execution_snapshots(
    execution_id: str,
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(500, ge=1, le=5000, description="每页数量"),
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取指定执行记录的快照数据（分页，按时间升序）
    """
    snapshots, total = await service.get_execution_snapshots(
        execution_id, page=page, page_size=page_size
    )
    return PerformanceSnapshotListResponse(
        total=total,
        items=[PerformanceSnapshotResponse(**s.to_dict()) for s in snapshots],
    )


@router.delete("/executions/{execution_id}", status_code=204)
async def delete_execution(
    execution_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    删除执行记录（级联删除关联快照）
    """
    try:
        deleted = await service.delete_execution(execution_id)
        if not deleted:
            raise HTTPException(status_code=404, detail="执行记录不存在")
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@router.get("/tasks/{task_id}/results", response_model=PerformanceTaskResponse)
async def get_task_results(
    task_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """获取性能测试任务执行结果详情"""
    task = await service.get_task(task_id)
    if not task:
        raise HTTPException(status_code=404, detail="任务不存在")
    return PerformanceTaskResponse(**task.to_dict())


# ==================== AI 分析 ====================


@router.post("/executions/{execution_id}/ai-analysis", response_model=AiAnalysisResponse)
async def ai_analysis_single(
    execution_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """对指定执行记录进行 AI 分析（瓶颈定位/优化建议）"""
    report = await service.get_execution_report(execution_id)
    if not report:
        raise HTTPException(status_code=404, detail="执行报告不存在")
    ai = PerformanceAiService()
    analysis = ai.analyze_single(report)
    return AiAnalysisResponse(
        execution_id=execution_id,
        analysis_type="single",
        overall_verdict=analysis.get("overall_verdict", ""),
        key_findings=analysis.get("key_findings", []),
        bottlenecks=analysis.get("bottlenecks", []),
        suggestions=analysis.get("suggestions", []),
        resource_analysis=analysis.get("resource_analysis"),
        comparison=analysis.get("comparison"),
        source=analysis.get("source", "ai"),
    )


@router.post("/ai-analysis/compare", response_model=AiAnalysisResponse)
async def ai_analysis_compare(
    req: AiAnalysisCompareRequest,
    service: PerformanceService = Depends(get_perf_service),
):
    """多版本对比 AI 分析"""
    reports = []
    for eid in req.execution_ids:
        r = await service.get_execution_report(eid)
        if not r:
            raise HTTPException(status_code=404, detail=f"执行记录不存在: {eid}")
        reports.append(r)
    reports.sort(key=lambda r: r["summary"].get("start_time") or "")
    ai = PerformanceAiService()
    analysis = ai.analyze_comparison(reports)
    return AiAnalysisResponse(
        execution_id=",".join(req.execution_ids),
        analysis_type="comparison",
        overall_verdict=analysis.get("overall_verdict", ""),
        key_findings=analysis.get("key_findings", []),
        bottlenecks=analysis.get("bottlenecks", []),
        suggestions=analysis.get("suggestions", []),
        comparison=analysis.get("comparison"),
        source=analysis.get("source", "ai"),
    )


# ==================== Phase 3: WebSocket 实时监控 ====================


@router.websocket("/ws/batch-progress")
async def websocket_batch_progress(websocket: WebSocket):
    """
    批量执行进度 WebSocket（固定分组，须注册在 /ws/{task_id} 之前）

    客户端连接后订阅 perf_batch 分组，实时收到批量执行的进度广播。
    消息格式（perf_batch_progress）:
    ```json
    {
        "type": "perf_batch_progress",
        "batch_id": "...",
        "project_id": "...",
        "current_index": 1,
        "total_count": 5,
        "current_task_id": "...",
        "current_task_name": "任务名",
        "current_status": "running",
        "success_count": 1,
        "fail_count": 0
    }
    ```
    current_status: running / completed / partially_failed / completed
    """
    ws_group = "perf_batch"
    await manager.connect(websocket, ws_group)

    try:
        # 保持连接，等待推送
        while True:
            try:
                data = await websocket.receive_text()
                msg = json.loads(data)
                if msg.get("type") == "ping":
                    await manager.send_to(websocket, {"type": "pong"})
            except json.JSONDecodeError:
                pass
    except WebSocketDisconnect:
        logger.info("批量进度 WebSocket 断开连接")
    except Exception as e:
        logger.error(f"批量进度 WebSocket 异常: {e}")
    finally:
        await manager.disconnect(websocket, ws_group)


@router.websocket("/ws/batch/{batch_id}")
async def websocket_batch_progress_specific(websocket: WebSocket, batch_id: str):
    """
    批量执行进度 WebSocket（按批次分组）

    客户端连接后订阅 perf_batch_{batch_id} 分组，只收到该批次的进度广播，
    避免多批次并发时全局分组串扰。
    连接建立后立即推送当前批次状态快照（若记录已存在），
    防止客户端错过连接前已完成的进度。

    消息格式（perf_batch_progress）:
    ```json
    {
        "type": "perf_batch_progress",
        "batch_id": "...",
        "project_id": "...",
        "current_index": 1,
        "total_count": 5,
        "current_task_id": "...",
        "current_task_name": "任务名",
        "current_status": "running",
        "success_count": 1,
        "fail_count": 0
    }
    ```
    """
    ws_group = f"perf_batch_{batch_id}"
    await manager.connect(websocket, ws_group)

    # 连接建立后推送当前批次状态快照（若记录已存在）
    try:
        async with async_session_maker() as db:
            service = PerformanceService(db)
            details = await service.get_batch_details(batch_id)
            if details:
                done_count = (details.get("success_count") or 0) + (details.get("fail_count") or 0)
                await manager.send_to(websocket, {
                    "type": "perf_batch_progress",
                    "batch_id": batch_id,
                    "project_id": details.get("project_id"),
                    "current_index": done_count,
                    "total_count": details.get("total_count") or 0,
                    "current_task_id": None,
                    "current_task_name": None,
                    "current_status": details.get("status") or "running",
                    "success_count": details.get("success_count") or 0,
                    "fail_count": details.get("fail_count") or 0,
                })
    except Exception as e:
        logger.warning(f"推送批量进度快照失败: {e}")

    try:
        # 保持连接，等待推送
        while True:
            try:
                data = await websocket.receive_text()
                msg = json.loads(data)
                if msg.get("type") == "ping":
                    await manager.send_to(websocket, {"type": "pong"})
            except json.JSONDecodeError:
                pass
    except WebSocketDisconnect:
        logger.info(f"批量进度 WebSocket 断开连接: batch_id={batch_id}")
    except Exception as e:
        logger.error(f"批量进度 WebSocket 异常: {e}")
    finally:
        await manager.disconnect(websocket, ws_group)


@router.websocket("/ws/{task_id}")
async def websocket_monitor(websocket: WebSocket, task_id: str):
    """
    性能测试实时监控 WebSocket

    客户端连接后，每秒收到服务端推送的性能快照数据。
    消息格式:
    ```json
    {
        "type": "perf_snapshot",
        "task_id": "...",
        "data": {
            "elapsed": 5.0,
            "tps": 120.5,
            "avg_response_time": 45.2,
            "p50": 32.1,
            "p90": 89.4,
            "p99": 245.6,
            "success_count": 120,
            "fail_count": 0,
            "total_requests": 600,
            "status_2xx": 120,
            "status_4xx": 0,
            "status_5xx": 0
        },
        "timestamp": "2026-08-12T12:00:00"
    }
    ```
    执行完成时推送:
    ```json
    {
        "type": "perf_complete",
        "task_id": "...",
        "status": "completed",
        "data": { ... }
    }
    ```
    """
    ws_group = f"perf_{task_id}"
    await manager.connect(websocket, ws_group)

    try:
        # 保持连接，等待推送
        while True:
            # 接收客户端消息（可处理暂停/恢复等指令）
            try:
                data = await websocket.receive_text()
                # 可以处理客户端发送的指令，目前仅保持连接
                msg = json.loads(data)
                if msg.get("type") == "ping":
                    await manager.send_to(websocket, {"type": "pong"})
            except json.JSONDecodeError:
                pass
    except WebSocketDisconnect:
        logger.info(f"WebSocket 断开连接: task_id={task_id}")
    except Exception as e:
        logger.error(f"WebSocket 异常: {e}")
    finally:
        await manager.disconnect(websocket, ws_group)


# ==================== 项目 CRUD ====================


@router.get("/projects", response_model=PerformanceProjectListResponse)
async def list_projects(
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(100, ge=1, le=200, description="每页数量"),
    project_type: Optional[str] = Query(None, description="按类型筛选: standard/custom"),
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取性能测试项目列表（分页）

    每个项目附带统计信息：任务数、最近执行状态、最近执行时间。
    """
    projects, total = await service.list_projects(page=page, page_size=page_size, project_type=project_type)

    items = []
    for proj in projects:
        proj_dict = proj.to_dict()
        # 获取项目统计
        stats = await service.get_project_stats(proj.id)
        proj_dict["task_count"] = stats["task_count"]
        proj_dict["last_run_status"] = stats["last_run_status"]
        proj_dict["last_run_time"] = stats["last_run_time"]
        items.append(PerformanceProjectResponse(**proj_dict))

    return PerformanceProjectListResponse(total=total, items=items)


@router.post("/projects", response_model=PerformanceProjectResponse, status_code=201)
async def create_project(
    data: PerformanceProjectCreate,
    service: PerformanceService = Depends(get_perf_service),
):
    """创建性能测试项目"""
    project = await service.create_project(data)
    result = project.to_dict()
    result["task_count"] = 0
    result["last_run_status"] = None
    result["last_run_time"] = None
    return PerformanceProjectResponse(**result)


@router.get("/projects/{project_id}", response_model=PerformanceProjectResponse)
async def get_project(
    project_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """获取性能测试项目详情"""
    project = await service.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="项目不存在")
    result = project.to_dict()
    stats = await service.get_project_stats(project.id)
    result["task_count"] = stats["task_count"]
    result["last_run_status"] = stats["last_run_status"]
    result["last_run_time"] = stats["last_run_time"]
    return PerformanceProjectResponse(**result)


@router.put("/projects/{project_id}", response_model=PerformanceProjectResponse)
async def update_project(
    project_id: str,
    data: PerformanceProjectUpdate,
    service: PerformanceService = Depends(get_perf_service),
):
    """更新性能测试项目"""
    project = await service.update_project(project_id, data.model_dump(exclude_unset=True))
    if not project:
        raise HTTPException(status_code=404, detail="项目不存在")
    result = project.to_dict()
    stats = await service.get_project_stats(project.id)
    result["task_count"] = stats["task_count"]
    result["last_run_status"] = stats["last_run_status"]
    result["last_run_time"] = stats["last_run_time"]
    return PerformanceProjectResponse(**result)


@router.delete("/projects/{project_id}", status_code=204)
async def delete_project(
    project_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """删除性能测试项目（不级联删除任务，仅置空 project_id）"""
    deleted = await service.delete_project(project_id)
    if not deleted:
        raise HTTPException(status_code=404, detail="项目不存在")


@router.get("/projects/{project_id}/stats", response_model=PerformanceProjectStatsResponse)
async def get_project_stats(
    project_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """获取性能测试项目统计信息"""
    project = await service.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="项目不存在")

    stats = await service.get_project_stats(project_id)
    return PerformanceProjectStatsResponse(**stats)


# ==================== 批量执行 ====================


@router.post("/tasks/batch-run", response_model=BatchRunResponse)
async def batch_run_tasks(
    data: BatchRunRequest,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    批量执行指定任务列表（非阻塞，立即返回）

    任务在后台异步执行，通过 WebSocket 推送进度。
    返回 batch_id 和 task_ids，前端可用于跳转到批量监控页。
    """
    if len(data.task_ids) == 0:
        raise HTTPException(status_code=400, detail="任务列表不能为空")

    # 先获取项目名称（如果传了 project_id）
    project_name = ""
    if data.project_id:
        project = await service.get_project(data.project_id)
        if project:
            project_name = project.name

    # 创建 batch 并在后台异步执行
    batch_id, task_count = await service.create_batch_and_start(
        task_ids=data.task_ids,
        project_id=data.project_id,
    )

    return BatchRunResponse(
        message="批量执行已启动",
        batch_id=batch_id,
        task_count=task_count,
        task_ids=data.task_ids,
        report_id=None,
    )


@router.post("/projects/{project_id}/run-all", response_model=BatchRunResponse)
async def run_project_all(
    project_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    执行项目下所有任务（非阻塞，立即返回）

    任务在后台异步执行，通过 WebSocket 推送进度。
    返回 batch_id 和 task_ids，前端可用于跳转到批量监控页。
    """
    project = await service.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="项目不存在")

    # 查询项目下可执行的任务 ID 列表
    task_ids = await service.get_project_task_ids(project_id)
    if not task_ids:
        raise HTTPException(status_code=400, detail="项目下没有可执行的任务")

    # 创建 batch 并在后台异步执行
    batch_id, task_count = await service.create_batch_and_start(
        task_ids=task_ids,
        project_id=project_id,
    )

    return BatchRunResponse(
        message="项目执行已启动",
        batch_id=batch_id,
        task_count=task_count,
        task_ids=task_ids,
        report_id=None,
    )


@router.get("/batch-details/{batch_id}", response_model=BatchDetailResponse)
async def get_batch_details(
    batch_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取批量执行详情

    返回批量执行记录的当前状态和各任务的执行状态/指标。
    前端 BatchMonitor 页面用于初始化任务列表和轮询状态。
    """
    details = await service.get_batch_details(batch_id)
    if not details:
        raise HTTPException(status_code=404, detail="批量执行记录不存在")

    return BatchDetailResponse(**details)


# ==================== 合并报告 ====================


@router.get("/projects/{project_id}/report", response_model=ProjectReportResponse)
async def get_project_report(
    project_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取项目最新合并报告

    返回该项目最近一次批量执行生成的合并报告。
    """
    report = await service.get_project_report(project_id)
    if not report:
        raise HTTPException(status_code=404, detail="该项目暂无合并报告")
    return report


@router.get("/batch-reports/{batch_id}", response_model=ProjectReportResponse)
async def get_batch_report(
    batch_id: str,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    按批次ID获取合并报告
    """
    report = await service.get_batch_report(batch_id)
    if not report:
        raise HTTPException(status_code=404, detail="合并报告不存在")
    return report


@router.get("/projects/{project_id}/reports", response_model=ProjectReportListResponse)
async def get_project_reports(
    project_id: str,
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(20, ge=1, le=100, description="每页数量"),
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取项目历史合并报告列表
    """
    result = await service.list_project_reports(project_id, page, page_size)
    return ProjectReportListResponse(**result)