"""
队列模块 - 队列管理、任务处理、队列统计和监控、任务发送
提供轻量级的路由入口，业务逻辑在 QueueService 中实现
包含跨语言任务发送 API
"""
from fastapi import APIRouter, HTTPException, Request, Query, Path, Depends
from typing import Optional, List, Dict, Any
from datetime import datetime

from jettask.schemas import (
    SendTasksRequest,
    SendTasksResponse
)
from jettask.core.message import TaskMessage
from jettask.core.namespace import NamespaceContext
from jettask.webui.deps import NsDep, get_ns
from jettask.webui.services.metrics_service import MetricsService, RunsMetric
from jettask.webui.services.queue_service import QueueService
import base64
import json
from jettask.utils.task_logger import LogContext, get_task_logger

router = APIRouter(prefix="/queues", tags=["队列"])
logger = get_task_logger(__name__)



@router.get(
    "",
    summary="获取命名空间队列列表",
    description="""获取指定命名空间下所有基础队列的列表。

**主要功能**:
- 从 QueueRegistry 获取所有注册的队列
- 自动过滤优先级队列，只返回基础队列名称
- 实时从 Redis 获取数据

**使用场景**: 队列管理页面列表、队列状态监控、队列选择器、管理界面展示""",
    responses={
        200: {
            "description": "成功返回队列列表",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "namespace": "default",
                        "queues": [
                            {"name": "email_queue"},
                            {"name": "sms_queue"}
                        ],
                        "total": 2
                    }
                }
            }
        },
        500: {"description": "服务器内部错误"}
    }
)
async def get_queues(ns: NamespaceContext = Depends(get_ns)) -> Dict[str, Any]:
    try:
        registry = await ns.get_queue_registry()

        base_queues = await registry.get_base_queues()

        queues_list = []
        for queue_name in sorted(base_queues):
            if queue_name == "TASK_CHANGES":
                continue
            queues_list.append({
                "name": queue_name
            })

        logger.info(f"获取命名空间 {ns.name} 的队列列表成功，共 {len(queues_list)} 个队列")

        return {
            "success": True,
            "namespace": ns.name,
            "queues": queues_list,
            "total": len(queues_list)
        }

    except Exception as e:
        logger.error(f"获取队列列表失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))




@router.get(
    "/{queue}/backlog",
    summary="获取队列积压情况",
    description="""获取指定队列的任务积压数量，用于监控队列健康状态。

**积压计算**:
- 积压数 = 发送offset - 消费确认offset
- pending_read = 发送offset - 读取offset

**返回字段说明**:
- total_backlog: 总积压数量（待消费确认的任务数）
- total_pending_read: 待读取数量（已发送但未被 Worker 读取的任务数）
- send_offset: 队列的发送 offset（累计发送消息数）
- tasks: 每个任务类型的详细积压情况

**使用场景**: 队列健康监控、积压告警、容量规划""",
    responses={
        200: {
            "description": "成功返回队列积压数据",
            "content": {
                "application/json": {
                    "examples": {
                        "队列总积压": {
                            "value": {
                                "success": True,
                                "queue": "email_queue",
                                "total_backlog": 156,
                                "total_pending_read": 20,
                                "send_offset": 10000,
                                "tasks": [
                                    {
                                        "task_name": "send_email",
                                        "ack_offset": 9800,
                                        "backlog": 100,
                                        "read_offset": 9900,
                                        "pending_read": 10
                                    },
                                    {
                                        "task_name": "send_sms",
                                        "ack_offset": 9944,
                                        "backlog": 56,
                                        "read_offset": 9990,
                                        "pending_read": 10
                                    }
                                ]
                            }
                        },
                        "指定任务积压": {
                            "value": {
                                "success": True,
                                "queue": "email_queue",
                                "task_name": "send_email",
                                "send_offset": 10000,
                                "ack_offset": 9800,
                                "backlog": 200,
                                "read_offset": 9900,
                                "pending_read": 100
                            }
                        }
                    }
                }
            }
        },
        500: {"description": "服务器内部错误"}
    }
)
async def get_queue_backlog(
    ns: NsDep,
    queue: str = Path(..., description="队列名称", example="email_queue"),
    task_name: Optional[str] = Query(None, description="任务名称（可选，不指定则返回所有任务的积压）")
) -> Dict[str, Any]:
    try:
        app = await ns.get_app()
        result = await app.get_queue_backlog(queue, task_name=task_name, asyncio=True)

        if "error" in result:
            raise HTTPException(status_code=500, detail=result["error"])

        return {
            "success": True,
            **result
        }

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取队列积压失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))




@router.get(
    "/backlog/trend",
    summary="获取积压趋势数据",
    description="""从 queue_snapshot 表查询积压趋势数据。

**数据来源**:
- queue_snapshot 表，每分钟采集一次
- 与任务执行解耦，即使没有任务执行也能记录积压

**聚合模式**:
- `aggregate_by_queue=true`: 返回队列级汇总
- `aggregate_by_queue=false`: 返回任务级明细

**时间间隔自动计算**:
- ≤ 1 小时：1 分钟
- 1-6 小时：5 分钟
- 6-24 小时：15 分钟
- 1-7 天：1 小时
- > 7 天：6 小时

**使用场景**: 积压监控、容量规划、告警分析""",
    responses={
        200: {
            "description": "成功返回积压趋势数据",
            "content": {
                "application/json": {
                    "examples": {
                        "队列级汇总": {
                            "value": {
                                "success": True,
                                "data": [
                                    {
                                        "queue": "email_queue",
                                        "trend": [
                                            {"time": "2026-01-22T10:00:00Z", "backlog": 100, "pending_read": 20},
                                            {"time": "2026-01-22T10:01:00Z", "backlog": 95, "pending_read": 15}
                                        ]
                                    }
                                ],
                                "granularity": "minute",
                                "time_range": {
                                    "start_time": "2026-01-22T10:00:00Z",
                                    "end_time": "2026-01-22T11:00:00Z",
                                    "interval": "1 minute",
                                    "interval_seconds": 60
                                }
                            }
                        },
                        "任务级明细": {
                            "value": {
                                "success": True,
                                "data": [
                                    {
                                        "queue": "email_queue",
                                        "tasks": [
                                            {
                                                "task_name": "send_email",
                                                "trend": [
                                                    {"time": "2026-01-22T10:00:00Z", "backlog": 50, "pending_read": 10}
                                                ]
                                            }
                                        ]
                                    }
                                ]
                            }
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_backlog_trend(
    ns: NsDep,
    queues: str = Query(..., description="队列名称，多个用逗号分隔", example="email_queue,sms_queue"),
    task_names: Optional[str] = Query(None, description="任务名称筛选，多个用逗号分隔"),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围，如 1h, 24h, 7d", example="1h"),
    aggregate_by_queue: bool = Query(False, description="是否按队列聚合（True 返回队列级汇总，False 返回任务级明细）")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()

        try:
            queue_list = [q.strip() for q in queues.split(',') if q.strip()]
            if not queue_list:
                raise HTTPException(status_code=400, detail="queues 参数不能为空")

            task_name_list = None
            if task_names:
                task_name_list = [t.strip() for t in task_names.split(',') if t.strip()]

            result = await QueueService.get_backlog_trend(
                namespace=ns.name,
                pg_session=pg_session,
                queues=queue_list,
                task_names=task_name_list,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                aggregate_by_queue=aggregate_by_queue
            )
            return result

        finally:
            await pg_session.close()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取积压趋势失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/backlog/summary",
    summary="获取当前积压汇总",
    description="""获取最新的积压汇总数据。

**数据来源**:
- 查询 queue_snapshot 表中最新一条快照
- 返回各队列各任务的当前积压情况

**使用场景**: 仪表盘展示、实时监控""",
    responses={
        200: {
            "description": "成功返回积压汇总数据",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": [
                            {
                                "queue": "email_queue",
                                "task_name": "send_email",
                                "backlog": 100,
                                "pending_read": 20,
                                "send_offset": 10000,
                                "ack_offset": 9900,
                                "read_offset": 9980
                            }
                        ],
                        "latest_time": "2026-01-22T10:05:00Z"
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_backlog_summary(
    ns: NsDep,
    queues: str = Query(..., description="队列名称，多个用逗号分隔", example="email_queue,sms_queue"),
    task_names: Optional[str] = Query(None, description="任务名称筛选，多个用逗号分隔")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()

        try:
            queue_list = [q.strip() for q in queues.split(',') if q.strip()]
            if not queue_list:
                raise HTTPException(status_code=400, detail="queues 参数不能为空")

            task_name_list = None
            if task_names:
                task_name_list = [t.strip() for t in task_names.split(',') if t.strip()]

            result = await QueueService.get_backlog_summary(
                namespace=ns.name,
                pg_session=pg_session,
                queues=queue_list,
                task_names=task_name_list
            )
            return result

        finally:
            await pg_session.close()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取积压汇总失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))




@router.get(
    "/{queue}/analytics/duration-distribution",
    summary="获取任务耗时分布",
    description="""获取指定队列的任务耗时分布数据，包括分位数（P50/P90/P99）和直方图。

**数据来源**:
- 从 task_runs_metrics_minute 聚合表查询直方图桶数据
- 分位数基于直方图桶数据估算（线性插值法）

**直方图桶划分**:
- 0 ~ 100ms
- 100ms ~ 500ms
- 500ms ~ 1s
- 1s ~ 5s
- > 5s

**使用场景**: 性能监控、SLA 分析、瓶颈定位""",
    responses={
        200: {
            "description": "成功返回耗时分布数据",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "percentiles": {
                            "p50": 156.0,
                            "p90": 890.0,
                            "p99": 2300.0
                        },
                        "histogram": [
                            {"range": "0-100ms", "count": 1200},
                            {"range": "100-500ms", "count": 800},
                            {"range": "500ms-1s", "count": 300},
                            {"range": "1s-5s", "count": 100},
                            {"range": ">5s", "count": 20}
                        ],
                        "time_range": {
                            "start_time": "2025-01-22T00:00:00Z",
                            "end_time": "2025-01-22T23:59:59Z"
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_duration_distribution(
    ns: NsDep,
    queue: str = Path(..., description="队列名称", example="email_queue"),
    task_names: Optional[str] = Query(None, description="任务名称，多个用逗号分隔"),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围：1h/6h/12h/24h/7d/30d", example="1h")
) -> Dict[str, Any]:
    try:
        registry = await ns.get_queue_registry()
        pg_session = await ns.get_pg_session()

        task_name_list = [t.strip() for t in task_names.split(",")] if task_names else None

        result = await MetricsService.get_duration_distribution(
            namespace=ns.name,
            pg_session=pg_session,
            registry=registry,
            queue=queue,
            task_names=task_name_list,
            start_time=start_time,
            end_time=end_time,
            time_range=time_range
        )

        if not result.get("success", True):
            raise HTTPException(status_code=500, detail=result.get("error", "未知错误"))

        return result

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取耗时分布失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/{queue}/analytics/error-stats",
    summary="获取错误类型统计",
    description="""获取指定队列的错误类型统计（Top N）。

**数据来源**:
- 从 task_runs 表查询 error_type 字段
- error_type 在任务执行失败时自动记录（异常类名）

**使用场景**: 错误分析、问题定位、稳定性监控""",
    responses={
        200: {
            "description": "成功返回错误类型统计数据",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": [
                            {"error_type": "TimeoutError", "count": 45, "percentage": 45.0},
                            {"error_type": "ValueError", "count": 23, "percentage": 23.0},
                            {"error_type": "ConnectionError", "count": 18, "percentage": 18.0},
                            {"error_type": "RateLimitError", "count": 10, "percentage": 10.0},
                            {"error_type": "其他", "count": 4, "percentage": 4.0}
                        ],
                        "total_errors": 100,
                        "time_range": {
                            "start_time": "2025-01-22T00:00:00Z",
                            "end_time": "2025-01-22T23:59:59Z"
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_error_stats(
    ns: NsDep,
    queue: str = Path(..., description="队列名称", example="email_queue"),
    task_names: Optional[str] = Query(None, description="任务名称，多个用逗号分隔"),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围：1h/6h/12h/24h/7d/30d", example="1h"),
    limit: int = Query(5, description="返回的错误类型数量", ge=1, le=20)
) -> Dict[str, Any]:
    try:
        registry = await ns.get_queue_registry()
        pg_session = await ns.get_pg_session()

        task_name_list = [t.strip() for t in task_names.split(",")] if task_names else None

        result = await MetricsService.get_error_stats(
            namespace=ns.name,
            pg_session=pg_session,
            registry=registry,
            queue=queue,
            task_names=task_name_list,
            start_time=start_time,
            end_time=end_time,
            time_range=time_range,
            limit=limit
        )

        if not result.get("success", True):
            raise HTTPException(status_code=500, detail=result.get("error", "未知错误"))

        return result

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取错误类型统计失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))




@router.get(
    "/metrics/tasks/summary",
    summary="任务提交汇总统计",
    description="""从 task_metrics_minute 聚合表查询任务提交/入队的汇总统计。

**功能说明**:
- 统计时间范围内各队列的任务提交总量
- 自动将基础队列名展开为所有优先级队列

**使用场景**: 流量监控、容量规划、队列负载分析""",
    responses={
        200: {
            "description": "成功返回任务提交汇总数据",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": [
                            {"queue_name": "email_queue", "task_count": 15000},
                            {"queue_name": "sms_queue", "task_count": 8000}
                        ],
                        "time_range": {
                            "start_time": "2025-01-22T00:00:00Z",
                            "end_time": "2025-01-22T23:59:59Z"
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_metrics_tasks_summary(
    ns: NsDep,
    queues: str = Query(..., description="队列名称，多个用逗号分隔", example="email_queue,sms_queue"),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围，如 15m, 1h, 24h, 7d", example="1h")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()
        registry = await ns.get_queue_registry()

        try:
            queue_list = [q.strip() for q in queues.split(',') if q.strip()]
            if not queue_list:
                raise HTTPException(status_code=400, detail="queues 参数不能为空")

            result = await MetricsService.get_tasks_summary(
                namespace=ns.name,
                pg_session=pg_session,
                registry=registry,
                queues=queue_list,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range
            )
            return result

        finally:
            await pg_session.close()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务提交汇总失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/metrics/tasks/trend",
    summary="任务提交时序趋势",
    description="""从 task_metrics_minute 聚合表查询任务提交的时序趋势数据。

**功能说明**:
- 按时间粒度返回任务提交数量的时序数据
- 时间间隔自动根据查询范围计算
- 自动将基础队列名展开为所有优先级队列

**时间间隔自动计算**:
- ≤ 1 小时：1 分钟
- 1-6 小时：5 分钟
- 6-24 小时：15 分钟
- 1-7 天：1 小时
- > 7 天：6 小时

**使用场景**: 任务提交速率监控、流量分析、峰值检测""",
    responses={
        200: {
            "description": "成功返回任务提交趋势数据",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": [
                            {
                                "queue_name": "email_queue",
                                "trend": [
                                    {"time": "2025-01-22T10:00:00Z", "task_count": 100},
                                    {"time": "2025-01-22T10:05:00Z", "task_count": 150}
                                ]
                            }
                        ],
                        "granularity": "5m",
                        "time_range": {
                            "start_time": "2025-01-22T10:00:00Z",
                            "end_time": "2025-01-22T11:00:00Z",
                            "interval": "5 minutes",
                            "interval_seconds": 300
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_metrics_tasks_trend(
    ns: NsDep,
    queues: str = Query(..., description="队列名称，多个用逗号分隔", example="email_queue,sms_queue"),
    task_names: Optional[str] = Query(None, description="任务名称筛选，多个用逗号分隔"),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围，如 15m, 1h, 24h, 7d", example="1h")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()
        registry = await ns.get_queue_registry()

        try:
            queue_list = [q.strip() for q in queues.split(',') if q.strip()]
            if not queue_list:
                raise HTTPException(status_code=400, detail="queues 参数不能为空")

            task_name_list = None
            if task_names:
                task_name_list = [t.strip() for t in task_names.split(',') if t.strip()]

            result = await MetricsService.get_tasks_trend(
                namespace=ns.name,
                pg_session=pg_session,
                registry=registry,
                queues=queue_list,
                task_names=task_name_list,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range
            )
            return result

        finally:
            await pg_session.close()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务提交趋势失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/metrics/runs/summary",
    summary="任务执行汇总统计",
    description="""从 task_runs_metrics_minute 聚合表查询任务执行情况的汇总统计。

**功能说明**:
- 统计时间范围内各队列下每个任务类型的执行情况
- 支持指定返回的指标字段
- 支持 cursor 分页（任务类型较多时）
- 支持按队列聚合（aggregate_tasks=true 时忽略 task_name 维度）

**支持的指标**:
- 计数类：total_count, success_count, failed_count, retry_count
- 耗时类：avg_duration, max_duration, min_duration（单位：秒）
- 延迟类：avg_delay, max_delay, min_delay（单位：秒，表示任务从创建到开始执行的等待时间）
- 并发类：max_concurrency（单位：任务数，表示单分钟内开始执行的任务数峰值，非实时并发）
- 计算类：success_rate（单位：百分比）

**指标详细说明**:
- total_count: 总执行次数（一个任务可能因重试被执行多次）
- success_count / failed_count: 成功/失败的执行次数
- retry_count: 重试次数累计
- avg_duration: 平均执行耗时（秒），从任务开始执行到完成的时间
- max_duration / min_duration: 执行耗时的最大/最小值（秒）
- avg_delay: 平均延迟（秒），从任务触发时间到实际开始执行的等待时间
- max_delay / min_delay: 延迟的最大/最小值（秒）
- max_concurrency: 单分钟内开始执行的任务数峰值（注意：这是近似并发指标，表示某分钟内有多少任务开始执行，而非同一时刻正在运行的任务数）
- success_rate: 成功率 = success_count / (success_count + failed_count) * 100

**aggregate_tasks 参数**:
- false（默认）：按 queue + task_name 粒度返回，包含 tasks 数组
- true：按 queue 粒度返回，聚合所有 task_name，直接返回指标字段

**使用场景**: 监控面板、队列健康状态、任务性能分析""",
    responses={
        200: {
            "description": "成功返回任务执行汇总数据",
            "content": {
                "application/json": {
                    "examples": {
                        "按任务分组（默认）": {
                            "value": {
                                "success": True,
                                "data": [
                                    {
                                        "queue_name": "email_queue",
                                        "task_count": 2,
                                        "tasks": [
                                            {
                                                "task_name": "send_email",
                                                "total_count": 10000,
                                                "success_count": 9950,
                                                "failed_count": 50,
                                                "success_rate": 99.5
                                            }
                                        ]
                                    }
                                ],
                                "time_range": {
                                    "start_time": "2025-01-22T00:00:00Z",
                                    "end_time": "2025-01-22T23:59:59Z"
                                },
                                "pagination": {
                                    "next_cursor": None,
                                    "has_more": False
                                }
                            }
                        },
                        "按队列聚合（aggregate_tasks=true）": {
                            "value": {
                                "success": True,
                                "data": [
                                    {
                                        "queue_name": "email_queue",
                                        "total_count": 15000,
                                        "success_count": 14900,
                                        "failed_count": 100,
                                        "retry_count": 50,
                                        "success_rate": 99.33,
                                        "avg_duration": 1.25,
                                        "max_duration": 15.6,
                                        "avg_delay": 0.5,
                                        "max_delay": 5.2,
                                        "max_concurrency": 50
                                    }
                                ],
                                "time_range": {
                                    "start_time": "2025-01-22T00:00:00Z",
                                    "end_time": "2025-01-22T23:59:59Z"
                                }
                            }
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_metrics_runs_summary(
    ns: NsDep,
    queues: str = Query(..., description="队列名称，多个用逗号分隔", example="email_queue,sms_queue"),
    task_names: Optional[str] = Query(None, description="任务名称筛选，多个用逗号分隔"),
    metrics: Optional[str] = Query(
        None,
        description="指定返回的指标，逗号分隔，默认返回全部。"
                    "支持: total_count, success_count, failed_count, retry_count, "
                    "avg_duration, max_duration, min_duration, avg_delay, max_delay, min_delay, "
                    "max_concurrency, success_rate",
        example="total_count,success_count,success_rate"
    ),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围，如 15m, 1h, 24h, 7d", example="1h"),
    aggregate_tasks: bool = Query(False, description="是否按队列聚合（忽略 task_name 维度）"),
    limit: int = Query(50, description="每页返回的任务数量", ge=1, le=200),
    cursor: Optional[str] = Query(None, description="分页游标")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()
        registry = await ns.get_queue_registry()

        try:
            queue_list = [q.strip() for q in queues.split(',') if q.strip()]
            if not queue_list:
                raise HTTPException(status_code=400, detail="queues 参数不能为空")

            task_name_list = None
            if task_names:
                task_name_list = [t.strip() for t in task_names.split(',') if t.strip()]

            metric_list = None
            if metrics:
                metric_list = []
                for m in metrics.split(','):
                    m = m.strip()
                    try:
                        metric_list.append(RunsMetric(m))
                    except ValueError:
                        raise HTTPException(
                            status_code=400,
                            detail=f"无效的指标名称: {m}。支持: {', '.join([e.value for e in RunsMetric])}"
                        )

            cursor_data = None
            if cursor:
                try:
                    cursor_data = json.loads(base64.b64decode(cursor).decode('utf-8'))
                except Exception:
                    raise HTTPException(status_code=400, detail="无效的 cursor")

            result = await MetricsService.get_runs_summary(
                namespace=ns.name,
                pg_session=pg_session,
                registry=registry,
                queues=queue_list,
                task_names=task_name_list,
                metrics=metric_list,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                aggregate_tasks=aggregate_tasks,
                limit=limit,
                cursor=cursor_data
            )
            return result

        finally:
            await pg_session.close()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务执行汇总失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/metrics/runs/trend",
    summary="任务执行时序趋势",
    description="""从 task_runs_metrics_minute 聚合表查询任务执行的时序趋势数据。

**功能说明**:
- 按时间粒度返回任务执行指标的时序数据
- 支持选择性聚合指标（节省算力）
- 时间间隔自动根据查询范围计算

**支持的指标**:
- 计数类：total_count, success_count, failed_count, retry_count
- 耗时类：avg_duration, max_duration, min_duration
- 延迟类：avg_delay, max_delay, min_delay
- 并发类：max_concurrency
- 计算类：success_rate, error_rate
- 分位数类：duration_percentiles (返回 duration_p50/p90/p99), delay_percentiles (返回 delay_p50/p90/p99)
- 积压类：backlog (返回 backlog, pending_read)

**时间间隔自动计算**:
- ≤ 1 小时：1 分钟
- 1-6 小时：5 分钟
- 6-24 小时：15 分钟
- 1-7 天：1 小时
- > 7 天：6 小时

**使用场景**: 执行速率监控、成功率趋势、延迟监控、并发量监控""",
    responses={
        200: {
            "description": "成功返回任务执行趋势数据",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": [
                            {
                                "queue_name": "email_queue",
                                "trend": [
                                    {
                                        "time": "2025-01-22T10:00:00Z",
                                        "total_count": 100,
                                        "success_count": 98
                                    }
                                ]
                            }
                        ],
                        "metrics": ["total_count", "success_count"],
                        "granularity": "5m",
                        "time_range": {
                            "start_time": "2025-01-22T10:00:00Z",
                            "end_time": "2025-01-22T11:00:00Z",
                            "interval": "5 minutes",
                            "interval_seconds": 300
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_metrics_runs_trend(
    ns: NsDep,
    queues: Optional[str] = Query(None, description="队列名称，多个用逗号分隔", example="email_queue,sms_queue"),
    queue: Optional[str] = Query(None, description="队列名称（单个，兼容旧版）", example="email_queue"),
    task_names: Optional[str] = Query(None, description="任务名称筛选，多个用逗号分隔"),
    metrics: Optional[str] = Query(
        None,
        description="指定聚合的指标，逗号分隔，默认 total_count。"
                    "支持: total_count, success_count, failed_count, retry_count, "
                    "avg_duration, max_duration, min_duration, avg_delay, max_delay, min_delay, "
                    "max_concurrency, success_rate, duration_percentiles, delay_percentiles, backlog, error_rate",
        example="total_count,success_count,duration_percentiles"
    ),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围，如 15m, 1h, 24h, 7d", example="1h")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()
        registry = await ns.get_queue_registry()

        try:
            queues_param = queues or queue
            if not queues_param:
                raise HTTPException(status_code=400, detail="queues 或 queue 参数不能为空")
            queue_list = [q.strip() for q in queues_param.split(',') if q.strip()]
            if not queue_list:
                raise HTTPException(status_code=400, detail="queues 或 queue 参数不能为空")

            task_name_list = None
            if task_names:
                task_name_list = [t.strip() for t in task_names.split(',') if t.strip()]

            metric_list = None
            if metrics:
                metric_list = []
                for m in metrics.split(','):
                    m = m.strip()
                    try:
                        metric_list.append(RunsMetric(m))
                    except ValueError:
                        raise HTTPException(
                            status_code=400,
                            detail=f"无效的指标名称: {m}。支持: {', '.join([e.value for e in RunsMetric])}"
                        )

            result = await MetricsService.get_runs_trend(
                namespace=ns.name,
                pg_session=pg_session,
                registry=registry,
                queues=queue_list,
                task_names=task_name_list,
                metrics=metric_list,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range
            )
            return result

        finally:
            await pg_session.close()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务执行趋势失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))




@router.get(
    "/runs",
    summary="任务执行记录列表",
    description="""从 tasks + task_runs 表联查，获取任务执行的详细记录。

**功能说明**:
- 支持复杂的 SQL 条件筛选
- 使用 cursor 分页，适合大数据量场景
- 返回轻量级数据，不包含 payload/result/error 等大字段
- 自动将基础队列名展开为所有优先级队列

**查询条件**:
- queue: 必填，单个队列名称
- start_time/end_time/time_range: 时间范围
- status: 状态筛选，多个用逗号分隔
- task_names: 任务名称筛选，多个用逗号分隔
- where_clause: 自定义 SQL 条件（高级功能）

**where_clause 可用字段**:
- stream_id, priority, created_at, trigger_time, source
- task_name, status, started_at, completed_at, retries, duration, consumer

**where_clause 示例**:
- `stream_id = '1769116820603'`
- `status = 'failed' AND duration > 10`
- `source = 'api' AND consumer LIKE 'worker-1%'`

**使用场景**: 任务执行历史查询、问题排查、性能分析""",
    responses={
        200: {
            "description": "成功返回任务执行记录",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": [
                            {
                                "stream_id": "1769065763365-0",
                                "task_name": "send_notification",
                                "status": "success",
                                "priority": 1,
                                "retries": 0,
                                "created_at": "2026-01-22T10:00:00Z",
                                "started_at": "2026-01-22T10:00:01Z",
                                "completed_at": "2026-01-22T10:00:02Z",
                                "duration": 1.05,
                                "delay": 1.0,
                                "consumer": "worker-1",
                                "source": "api"
                            }
                        ],
                        "queue": "email_queue",
                        "time_range": {
                            "start_time": "2026-01-22T09:00:00Z",
                            "end_time": "2026-01-22T10:00:00Z"
                        },
                        "pagination": {
                            "next_cursor": "eyJzdHJlYW1faWQiOiIxNzY5MDY1NzYzMzY1LTAifQ==",
                            "has_more": True,
                            "estimated_total": 1234
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_task_runs_list(
    ns: NsDep,
    queue: str = Query(..., description="队列名称（必填）", example="email_queue"),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围，如 1h, 24h, 7d", example="1h"),
    status: Optional[str] = Query(None, description="状态筛选，多个用逗号分隔。支持: pending, running, success, error, failed, delayed, rejected, retry, timeout, cancelled, scheduled", example="success,failed"),
    task_names: Optional[str] = Query(None, description="任务名称筛选，多个用逗号分隔"),
    where_clause: Optional[str] = Query(None, description="自定义 SQL WHERE 条件，如 status = 'failed' AND duration > 10"),
    limit: int = Query(50, description="每页数量", ge=1, le=200),
    cursor: Optional[str] = Query(None, description="分页游标"),
    sort_field: str = Query("created_at", description="排序字段"),
    sort_order: str = Query("desc", description="排序方向", regex="^(asc|desc)$"),
    include_fields: Optional[str] = Query(None, description="额外返回的大字段，多个用逗号分隔。支持: payload, result, error, metadata", example="error,payload")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()
        registry = await ns.get_queue_registry()

        try:
            status_list = None
            if status:
                status_list = [s.strip() for s in status.split(',') if s.strip()]

            task_name_list = None
            if task_names:
                task_name_list = [t.strip() for t in task_names.split(',') if t.strip()]

            include_field_list = None
            if include_fields:
                valid_fields = {'payload', 'result', 'error', 'metadata'}
                include_field_list = [f.strip() for f in include_fields.split(',') if f.strip() in valid_fields]

            result = await QueueService.get_task_runs_list(
                namespace=ns.name,
                pg_session=pg_session,
                registry=registry,
                queue=queue,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                status=status_list,
                task_names=task_name_list,
                where_clause=where_clause,
                limit=limit,
                cursor=cursor,
                sort_field=sort_field,
                sort_order=sort_order,
                include_fields=include_field_list
            )
            return result

        finally:
            await pg_session.close()

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"获取任务执行记录失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/runs/trend",
    summary="任务执行趋势（支持复杂条件和维度聚合）",
    description="""从 tasks + task_runs 表联查，获取任务执行趋势数据。

**功能说明**:
- 支持与 /runs 接口相同的查询条件
- 按时间桶聚合统计 total_count, success_count, failed_count
- 支持按指定维度分组聚合（group_by 参数）
- 时间粒度自动根据查询范围计算

**时间聚合说明**:
- 返回的 time 字段表示任务**开始执行时间**（started_at），而非入队时间（created_at）
- 每个时间桶内的统计是该时间段内开始执行的任务数量

**聚合维度（group_by）** - 支持多个，用逗号分隔:
- priority: 按优先级分组
- source: 按来源分组
- task_name: 按任务名称分组
- consumer: 按消费者分组
- status: 按状态分组

示例: `group_by=task_name,priority` 按任务名称和优先级组合分组

**where_clause 可用字段**:
- stream_id, priority, created_at, trigger_time, source
- task_name, status, started_at, completed_at, retries, duration, consumer

**where_clause 示例**:
- `stream_id = '1769116820603'`
- `status = 'failed' AND duration > 10`

**与 /metrics/runs/trend 的区别**:
- /metrics/runs/trend: 从预聚合表查询，查询快，但无法支持灵活的 SQL 条件
- /runs/trend: 从原始表查询，支持任意 SQL 条件和维度聚合，适合精确筛选场景

**使用场景**: 配合 /runs 接口使用，在同一页面展示符合条件的趋势图和任务列表""",
    responses={
        200: {
            "description": "成功返回任务执行趋势",
            "content": {
                "application/json": {
                    "examples": {
                        "无分组": {
                            "value": {
                                "success": True,
                                "data": {
                                    "trend": [
                                        {"time": "2026-01-22T10:00:00Z", "total_count": 100, "success_count": 95, "failed_count": 5},
                                        {"time": "2026-01-22T10:05:00Z", "total_count": 120, "success_count": 118, "failed_count": 2}
                                    ]
                                },
                                "queue": "email_queue",
                                "group_by": None,
                                "granularity": "5m",
                                "time_range": {
                                    "start_time": "2026-01-22T10:00:00Z",
                                    "end_time": "2026-01-22T11:00:00Z"
                                }
                            }
                        },
                        "按 task_name 分组": {
                            "value": {
                                "success": True,
                                "data": {
                                    "send_email": {
                                        "dimensions": {"task_name": "send_email"},
                                        "trend": [
                                            {"time": "2026-01-22T10:00:00Z", "total_count": 50, "success_count": 48, "failed_count": 2}
                                        ]
                                    },
                                    "send_sms": {
                                        "dimensions": {"task_name": "send_sms"},
                                        "trend": [
                                            {"time": "2026-01-22T10:00:00Z", "total_count": 50, "success_count": 47, "failed_count": 3}
                                        ]
                                    }
                                },
                                "queue": "email_queue",
                                "group_by": ["task_name"],
                                "granularity": "5m"
                            }
                        },
                        "按 task_name 和 priority 多维度分组": {
                            "value": {
                                "success": True,
                                "data": {
                                    "send_email|1": {
                                        "dimensions": {"task_name": "send_email", "priority": "1"},
                                        "trend": [
                                            {"time": "2026-01-22T10:00:00Z", "total_count": 30, "success_count": 28, "failed_count": 2}
                                        ]
                                    },
                                    "send_email|2": {
                                        "dimensions": {"task_name": "send_email", "priority": "2"},
                                        "trend": [
                                            {"time": "2026-01-22T10:00:00Z", "total_count": 20, "success_count": 20, "failed_count": 0}
                                        ]
                                    }
                                },
                                "queue": "email_queue",
                                "group_by": ["task_name", "priority"],
                                "granularity": "5m"
                            }
                        }
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_task_runs_trend(
    ns: NsDep,
    queue: str = Query(..., description="队列名称（必填）", example="email_queue"),
    start_time: Optional[datetime] = Query(None, description="开始时间（ISO格式）"),
    end_time: Optional[datetime] = Query(None, description="结束时间（ISO格式）"),
    time_range: Optional[str] = Query(None, description="时间范围，如 1h, 24h, 7d", example="1h"),
    status: Optional[str] = Query(None, description="状态筛选，多个用逗号分隔。支持: pending, running, success, error, failed, delayed, rejected, retry, timeout, cancelled, scheduled", example="success,failed"),
    task_names: Optional[str] = Query(None, description="任务名称筛选，多个用逗号分隔"),
    where_clause: Optional[str] = Query(None, description="自定义 SQL WHERE 条件，如 status = 'failed' AND duration > 10"),
    group_by: Optional[str] = Query(
        None,
        description="聚合维度，多个用逗号分隔。支持: priority, source, task_name, consumer, status",
        example="task_name,priority"
    )
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()
        registry = await ns.get_queue_registry()

        try:
            status_list = None
            if status:
                status_list = [s.strip() for s in status.split(',') if s.strip()]

            task_name_list = None
            if task_names:
                task_name_list = [t.strip() for t in task_names.split(',') if t.strip()]

            group_by_list = None
            if group_by:
                group_by_list = [g.strip() for g in group_by.split(',') if g.strip()]

            result = await QueueService.get_task_runs_trend(
                namespace=ns.name,
                pg_session=pg_session,
                registry=registry,
                queue=queue,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                status=status_list,
                task_names=task_name_list,
                where_clause=where_clause,
                group_by=group_by_list
            )
            return result

        finally:
            await pg_session.close()

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"获取任务执行趋势失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/runs/{stream_id}",
    summary="任务执行详情",
    description="""获取单个任务执行记录的完整详情。

**功能说明**:
- 包含 payload、result、error 等大字段
- 适合查看任务的完整执行信息

**使用场景**: 任务详情页面、错误排查、执行结果查看""",
    responses={
        200: {
            "description": "成功返回任务详情",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": {
                            "stream_id": "1769065763365-0",
                            "queue": "email_queue",
                            "task_name": "send_notification",
                            "status": "success",
                            "payload": {"to": "user@example.com", "subject": "Hello"},
                            "result": {"message_id": "abc123"},
                            "error": None,
                            "priority": 1,
                            "retries": 0,
                            "created_at": "2026-01-22T10:00:00Z",
                            "started_at": "2026-01-22T10:00:01Z",
                            "completed_at": "2026-01-22T10:00:02Z",
                            "duration": 1.05,
                            "delay_seconds": 1.0,
                            "consumer": "worker-1",
                            "source": "api"
                        }
                    }
                }
            }
        },
        404: {"description": "任务不存在"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_task_run_detail(
    ns: NsDep,
    stream_id: str = Path(..., description="任务 Stream ID")
) -> Dict[str, Any]:
    try:
        pg_session = await ns.get_pg_session()

        try:
            result = await QueueService.get_task_run_detail(
                namespace=ns.name,
                pg_session=pg_session,
                stream_id=stream_id
            )

            if not result.get("success") and "不存在" in result.get("error", ""):
                raise HTTPException(status_code=404, detail=result.get("error"))

            return result

        finally:
            await pg_session.close()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务详情失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))




@router.post(
    "/send-queue",
    summary="发送任务到队列",
    description="""跨语言的任务发送接口，支持从任何编程语言通过 HTTP API 发送任务。

**功能特性**: 支持批量发送多个任务、设置任务优先级、延迟执行、返回每个任务的事件 ID

**请求体参数**: messages 数组，每个 message 包含 queue（必需）、priority（可选，1最高）、delay（可选，秒）、kwargs（字典）、args（列表）

**使用场景**: Go/Java 服务发送任务到 Python worker、微服务跨语言任务调度、外部系统集成（Webhook、第三方服务）、定时任务触发器、批量数据处理任务

**注意**: 命名空间必须存在，priority: 1 是最高优先级，返回的 event_ids 可用于追踪任务状态""",
    response_model=SendTasksResponse,
    responses={
        200: {
            "description": "任务发送成功",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "message": "Tasks sent successfully",
                        "event_ids": [
                            "1730361234567-0",
                            "1730361234568-0"
                        ],
                        "total_sent": 2,
                        "namespace": "default"
                    }
                }
            }
        },
        400: {"description": "请求参数错误"},
        404: {"description": "命名空间不存在"},
        500: {"description": "服务器内部错误"}
    }
)
async def send_tasks(
    request: Request,
    send_request: SendTasksRequest,
    ns: NsDep
) -> SendTasksResponse:
    try:
        namespace = ns.name

        client_host = request.client.host if request.client else 'unknown'
        client_port = request.client.port if request.client else 'unknown'

        with LogContext(
            endpoint='/queues/send-queue',
            namespace=namespace,
            client_ip=client_host,
            client_port=client_port,
            task_count=len(send_request.messages)
        ):
            logger.info(
                f"收到任务发送请求",
                extra={
                    'extra_fields': {
                        'namespace': namespace,
                        'task_count': len(send_request.messages),
                        'client_ip': client_host,
                        'client_port': client_port,
                        'request_path': str(request.url.path),
                        'method': request.method,
                        'messages': [msg.model_dump() for msg in send_request.messages ]
                    }
                }
            )

            for idx, msg_req in enumerate(send_request.messages):
                logger.debug(
                    f"任务 #{idx+1} 详情",
                    extra={
                        'extra_fields': {
                            'task_index': idx + 1,
                            'queue': msg_req.queue,
                            'priority': msg_req.priority,
                            'delay': msg_req.delay,
                            'has_args': bool(msg_req.args),
                            'args_count': len(msg_req.args) if msg_req.args else 0,
                            'kwargs_keys': list(msg_req.kwargs.keys()) if msg_req.kwargs else [],
                            'kwargs': msg_req.kwargs
                        }
                    }
                )

            logger.debug(f"正在获取命名空间 '{namespace}' 的 Jettask 实例")
            jettask_app = await ns.get_jettask_app()

            task_messages = []
            for msg_req in send_request.messages:
                msg = TaskMessage(
                    queue=msg_req.queue,
                    kwargs=msg_req.kwargs,
                    priority=msg_req.priority,
                    delay=msg_req.delay,
                    args=tuple(msg_req.args) if msg_req.args else ()
                )
                task_messages.append(msg)

            logger.info(
                f"开始发送 {len(task_messages)} 个任务到命名空间 '{namespace}'"
            )
            event_ids = await jettask_app.send_tasks(task_messages, asyncio=True)

            if not event_ids:
                logger.error(
                    "任务发送失败: 未返回事件ID",
                    extra={
                        'extra_fields': {
                            'namespace': namespace,
                            'task_count': len(task_messages)
                        }
                    }
                )
                raise HTTPException(
                    status_code=500,
                    detail="Failed to send tasks: no event IDs returned"
                )

            logger.info(
                f"成功发送 {len(event_ids)} 个任务到命名空间 '{namespace}'",
                extra={
                    'extra_fields': {
                        'namespace': namespace,
                        'event_ids': event_ids,
                        'total_sent': len(event_ids),
                        'client_ip': client_host
                    }
                }
            )

            return SendTasksResponse(
                success=True,
                message="Tasks sent successfully",
                event_ids=event_ids,
                total_sent=len(event_ids),
                namespace=namespace
            )

    except HTTPException:
        raise
    except Exception as e:
        namespace_name = getattr(request.state, 'ns', None)
        namespace_name = namespace_name.name if namespace_name else 'unknown'

        logger.error(
            f"任务发送失败",
            extra={
                'extra_fields': {
                    'namespace': namespace_name,
                    'error': str(e),
                    'error_type': type(e).__name__
                }
            },
            exc_info=True
        )

        return SendTasksResponse(
            success=False,
            message="Failed to send tasks",
            event_ids=[],
            total_sent=0,
            namespace=namespace_name,
            error=str(e)
        )




@router.get(
    "/{queue}/tasks",
    summary="获取队列下的任务列表",
    description="""获取指定队列下注册的所有任务名称。

**数据来源**:
- 从 REGISTRY:TASKS Hash 读取（Worker 启动订阅队列时注册）
- 比 READ_OFFSETS 更可靠：即使任务未消费过，只要 Worker 启动过就能获取

**注意**: 如果 Worker 从未启动过，该队列下不会有任务记录。

**使用场景**: 任务配置管理、任务选择器、队列任务概览""",
    responses={
        200: {
            "description": "成功返回任务列表",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "namespace": "default",
                        "queue": "email_queue",
                        "tasks": ["send_email", "send_notification"],
                        "total": 2
                    }
                }
            }
        },
        500: {"description": "服务器内部错误"}
    }
)
async def get_queue_tasks(
    ns: NsDep,
    queue: str = Path(..., description="队列名称", example="email_queue"),
    exclude_internal: bool = Query(False, description="是否排除内部任务（如 _handle_persist_task）")
) -> Dict[str, Any]:
    try:
        registry = await ns.get_queue_registry()
        tasks = await registry.get_tasks_by_queue(queue)

        if exclude_internal:
            tasks = [t for t in tasks if not t.endswith('._handle_persist_task')]

        return {
            "success": True,
            "namespace": ns.name,
            "queue": queue,
            "tasks": sorted(tasks),
            "total": len(tasks)
        }
    except Exception as e:
        logger.error(f"获取队列任务列表失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))



from pydantic import BaseModel, Field


class TaskConfigResponse(BaseModel):
    """任务配置响应"""
    success: bool = True
    data: Dict[str, Any]


class UpdateTaskConfigRequest(BaseModel):
    """更新任务配置请求

    注意：priority（任务优先级）不在此处配置，优先级只能在发送任务时由客户端指定
    """
    rate_limit: Optional[Dict[str, Any]] = Field(None, description="限流配置，如 {type: 'qps', qps: 10, window_size: 1.0} 或 {type: 'concurrency', max_concurrency: 5}")
    max_retries: Optional[int] = Field(None, description="最大重试次数")
    retry_delay: Optional[int] = Field(None, description="重试延迟（秒）")


@router.get(
    "/{queue}/tasks/{task_name}/config",
    summary="获取任务配置",
    description="""获取指定任务的动态配置。

**配置项**:
- `rate_limit`: 限流配置（QPS 或并发限流，二选一）
- `max_retries`: 最大重试次数
- `retry_delay`: 重试延迟（秒）

**注意**: 任务优先级（priority）不在此处配置，优先级只能在发送任务时由客户端指定

**使用场景**: 任务详情页面、配置管理""",
    response_model=TaskConfigResponse,
)
async def get_task_config(
    request: Request,
    ns: NsDep,
    queue: str = Path(..., description="队列名称"),
    task_name: str = Path(..., description="任务名称")
) -> Dict[str, Any]:
    from jettask.config import DynamicConfig

    try:
        redis_client = await ns.get_redis_client()
        prefix = ns.redis_prefix

        config_key = DynamicConfig.get_task_config_key(prefix, task_name)
        data = await redis_client.hgetall(config_key)

        configs = {}
        for k, v in data.items():
            k = k.decode() if isinstance(k, bytes) else k
            v = v.decode() if isinstance(v, bytes) else v
            configs[k] = v

        return {
            "success": True,
            "data": {
                "namespace": ns.name,
                "queue": queue,
                "task_name": task_name,
                "configs": configs
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务配置失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.put(
    "/{queue}/tasks/{task_name}/config",
    summary="更新任务配置",
    description="""更新指定任务的动态配置。

配置会同步到对应命名空间的 Redis，Worker 会在下次同步时获取新配置。

**注意**:
- 配置只对新任务生效
- 已在队列中的任务不受影响
- Worker 同步延迟约 30 秒

**示例请求**:
```json
{
    "rate_limit": {"type": "qps", "qps": 10, "window_size": 1.0},
    "max_retries": 5,
    "retry_delay": 60
}
```

**限流配置说明**:
- QPS 限流: `{"type": "qps", "qps": 10, "window_size": 1.0}`
- 并发限流: `{"type": "concurrency", "max_concurrency": 5}`
- 每个任务只能配置一种限流类型""",
    response_model=TaskConfigResponse,
)
async def update_task_config(
    request: Request,
    ns: NsDep,
    queue: str = Path(..., description="队列名称"),
    task_name: str = Path(..., description="任务名称"),
    body: UpdateTaskConfigRequest = None
) -> Dict[str, Any]:
    from jettask.config import DynamicConfig
    from jettask.webui.services.config_service import ConfigService

    try:
        configs = {}
        if body:
            logger.info(f"收到任务配置更新请求: task={task_name}, body={body.model_dump()}")
            if body.rate_limit is not None:
                configs['rate_limit'] = body.rate_limit
            if body.max_retries is not None:
                configs['max_retries'] = body.max_retries
            if body.retry_delay is not None:
                configs['retry_delay'] = body.retry_delay

        if not configs:
            raise HTTPException(status_code=400, detail="至少需要提供一个配置项")

        redis_client = await ns.get_redis_client()
        prefix = ns.redis_prefix

        await DynamicConfig.set_task_config_to_redis(
            redis_client, prefix, task_name, configs
        )

        config_key = DynamicConfig.get_task_config_key(prefix, task_name)
        redis_task_version = await redis_client.hget(config_key, '_version')
        redis_task_version = int(redis_task_version) if redis_task_version else 0

        pg_session = await ns.get_pg_session()
        try:
            await ConfigService.save_task_config_to_pg(
                pg_session, ns.name, queue, task_name, configs
            )
            await ConfigService.set_task_version_in_pg(
                pg_session, ns.name, queue, task_name, redis_task_version
            )
        finally:
            await pg_session.close()

        return {
            "success": True,
            "data": {
                "namespace": ns.name,
                "queue": queue,
                "task_name": task_name,
                "configs": configs
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"更新任务配置失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.delete(
    "/{queue}/tasks/{task_name}/config",
    summary="删除任务配置",
    description="删除指定任务的动态配置，恢复使用代码中的默认值。",
)
async def delete_task_config(
    request: Request,
    ns: NsDep,
    queue: str = Path(..., description="队列名称"),
    task_name: str = Path(..., description="任务名称")
) -> Dict[str, Any]:
    from jettask.config import DynamicConfig
    from jettask.webui.services.config_service import ConfigService

    try:
        redis_client = await ns.get_redis_client()
        prefix = ns.redis_prefix

        await DynamicConfig.delete_task_config_from_redis(
            redis_client, prefix, task_name
        )

        pg_session = await ns.get_pg_session()
        try:
            await ConfigService.delete_task_config_from_pg(
                pg_session, ns.name, queue, task_name
            )
        finally:
            await pg_session.close()

        return {
            "success": True,
            "message": f"任务配置已删除: {ns.name}:{queue}:{task_name}"
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"删除任务配置失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get(
    "/{queue}/task-runs/{stream_id}",
    summary="获取任务执行详情",
    description="""获取单个任务的详细信息，包含 payload、result、error 等大字段。

**使用场景**: 在列表页点击某条任务后，加载完整详情。
列表接口不返回这些大字段以保证性能，需要通过此接口单独获取。

**可选字段**: 通过 `fields` 参数指定需要返回的字段，不指定则返回全部。
支持的字段: payload, result, error, metadata""",
    responses={
        200: {
            "description": "成功返回任务详情",
            "content": {
                "application/json": {
                    "example": {
                        "success": True,
                        "data": {
                            "stream_id": "1737945600000-0",
                            "queue": "email_queue",
                            "task_name": "send_email",
                            "status": "success",
                            "payload": {"to": "user@example.com"},
                            "result": {"message_id": "abc123"},
                            "error": None
                        }
                    }
                }
            }
        },
        404: {"description": "任务不存在"},
        500: {"description": "服务器内部错误"}
    }
)
async def get_task_run_detail(
    ns: NsDep,
    queue: str = Path(..., description="队列名称"),
    stream_id: str = Path(..., description="任务 Stream ID"),
    fields: Optional[List[str]] = Query(None, description="指定返回的大字段列表，如 payload, result, error, metadata")
) -> Dict[str, Any]:
    try:
        session = await ns.get_pg_session()
        async with session:
            result = await QueueService.get_task_run_detail(
                namespace=ns.name,
                pg_session=session,
                stream_id=stream_id
            )

        if not result.get("success"):
            raise HTTPException(status_code=404, detail=result.get("error", "任务不存在"))

        if fields and result.get("data"):
            large_fields = {"payload", "result", "error", "metadata"}
            requested = set(fields)
            for f in large_fields:
                if f not in requested and f in result["data"]:
                    result["data"][f] = None

        return result
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务详情失败: stream_id={stream_id}, error={e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


__all__ = ['router']