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

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

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
from app.schemas.performance import (
    PerformanceTaskCreate,
    PerformanceTaskUpdate,
    PerformanceTaskResponse,
    PerformanceTaskListItem,
    PerformanceTaskListResponse,
    PerformanceRunRequest,
    PerformanceRunResponse,
    PerformanceSnapshotResponse,
    PerformanceSnapshotListResponse,
    PerformanceReportResponse,
    PerformanceReportSummary,
    PerformanceReportMetrics,
    CurlParseRequest,
    CurlParseResponse,
    PerformanceProjectCreate,
    PerformanceProjectUpdate,
    PerformanceProjectResponse,
    PerformanceProjectListResponse,
    PerformanceProjectStatsResponse,
    BatchRunRequest,
    BatchRunResponse,
)
from app.services.performance_service import PerformanceService
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),
):
    """
    执行性能测试任务

    在后台线程池中运行压测引擎，执行完成后自动更新任务结果。
    支持通过 config_override 临时覆盖配置项。
    """
    if data is None:
        data = PerformanceRunRequest()

    try:
        result = await service.run_task(
            task_id,
            config_override=data.config_override,
        )
        return PerformanceRunResponse(
            message="任务执行完成" if result.get("status") == "completed" else "任务执行失败",
            task_id=task_id,
            status=result.get("status", "failed"),
        )
    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,
    service: PerformanceService = Depends(get_perf_service),
):
    """
    获取性能测试报告

    返回完整报告数据，包含执行摘要、指标统计和所有快照。
    """
    report = await service.get_report(task_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"]],
    )


@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())


# ==================== 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/{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 推送（perf_batch_progress）。
    某个任务失败不影响后续任务执行。
    """
    if len(data.task_ids) == 0:
        raise HTTPException(status_code=400, detail="任务列表不能为空")

    result = await service.batch_run_tasks(
        task_ids=data.task_ids,
        project_id=data.project_id,
    )

    return BatchRunResponse(
        message=f"批量执行完成: {result['success_count']} 成功, {result['fail_count']} 失败",
        batch_id=result["batch_id"],
        task_count=result["total"],
    )


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

    按创建时间顺序执行项目下所有 pending/completed/failed/cancelled 状态的任务。
    进度通过 WebSocket 推送（perf_batch_progress）。
    """
    project = await service.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="项目不存在")

    try:
        result = await service.run_project_all(project_id)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    return BatchRunResponse(
        message=f"项目执行完成: {result['success_count']} 成功, {result['fail_count']} 失败",
        batch_id=result["batch_id"],
        task_count=result["total"],
    )