"""
队列服务层
处理队列相关的业务逻辑
"""
from typing import Optional, List, Dict, Any
from datetime import datetime, timedelta, timezone
from sqlalchemy import text, select, func, and_
from sqlalchemy.ext.asyncio import AsyncSession
import logging

from jettask.db.models.task import Task
from jettask.db.models.task_run import TaskRun
from jettask.db.models.task_metrics_minute import TaskMetricsMinute
from jettask.db.models.queue_snapshot import QueueSnapshot
from jettask.webui.services.base_service import BaseService, TimeRangeResult, QueueResolutionResult
import base64
import json

logger = logging.getLogger(__name__)


class QueryConditionsResult:
    """查询条件构建结果"""
    def __init__(
        self,
        conditions: list,
        all_priority_queues: List[str],
        time_range_result: TimeRangeResult
    ):
        self.conditions = conditions
        self.all_priority_queues = all_priority_queues
        self.time_range_result = time_range_result


class QueueService(BaseService):
    """队列服务类"""
    
    @staticmethod
    def get_base_queue_name(queue_name: str) -> str:
        if ':' in queue_name:
            parts = queue_name.rsplit(':', 1)
            if parts[-1].isdigit():
                return parts[0]
        return queue_name
    
    @staticmethod
    async def get_queues_by_namespace(namespace_data_access, namespace: str) -> Dict[str, Any]:
        queues_data = await namespace_data_access.get_queue_stats(namespace)
        return {
            "success": True,
            "data": list(set([QueueService.get_base_queue_name(q['queue_name']) for q in queues_data]))
        }
    
    @staticmethod
    async def get_queue_flow_rates(data_access, query) -> Dict[str, Any]:
        now = datetime.now(timezone.utc)
        
        if query.start_time and query.end_time:
            start_time = query.start_time
            end_time = query.end_time
            logger.info(f"使用自定义时间范围: {start_time} 到 {end_time}")
        else:
            time_range_map = {
                "15m": timedelta(minutes=15),
                "30m": timedelta(minutes=30),
                "1h": timedelta(hours=1),
                "3h": timedelta(hours=3),
                "6h": timedelta(hours=6),
                "12h": timedelta(hours=12),
                "24h": timedelta(hours=24),
                "7d": timedelta(days=7),
                "30d": timedelta(days=30),
            }
            
            time_range_value = query.time_range if query.time_range else query.interval
            delta = time_range_map.get(time_range_value, timedelta(minutes=15))
            
            queue_name = query.queues[0] if query.queues else None
            if queue_name:
                latest_time = await data_access.get_latest_task_time(queue_name)
                if latest_time:
                    end_time = latest_time.replace(second=59, microsecond=999999)  
                    logger.info(f"使用最新任务时间: {latest_time}")
                else:
                    end_time = now.replace(second=0, microsecond=0)
            else:
                end_time = now.replace(second=0, microsecond=0)
            
            start_time = end_time - delta
            logger.info(f"使用预设时间范围 {time_range_value}: {start_time} 到 {end_time}, delta: {delta}")
        
        if not query.queues or len(query.queues) == 0:
            return {"data": [], "granularity": "minute"}
        
        queue_name = query.queues[0]
        filters = getattr(query, 'filters', None)
        data, granularity = await data_access.fetch_queue_flow_rates(
            queue_name, start_time, end_time, filters
        )
        
        return {"data": data, "granularity": granularity}
    
    @staticmethod
    async def get_global_stats(data_access) -> Dict[str, Any]:
        stats_data = await data_access.fetch_global_stats()
        return {
            "success": True,
            "data": stats_data
        }
    
    @staticmethod
    async def get_queues_detail(data_access) -> Dict[str, Any]:
        queues_data = await data_access.fetch_queues_data()
        return {
            "success": True,
            "data": queues_data
        }
    
    @staticmethod
    async def delete_queue(queue_name: str) -> Dict[str, Any]:
        logger.info(f"删除队列请求: {queue_name}")
        return {
            "success": True,
            "message": f"队列 {queue_name} 已删除"
        }
    
    @staticmethod
    async def trim_queue(queue_name: str, max_length: int) -> Dict[str, Any]:
        logger.info(f"裁剪队列请求: {queue_name}, 保留 {max_length} 条消息")
        return {
            "success": True,
            "message": f"队列 {queue_name} 已裁剪至 {max_length} 条消息"
        }
    
    @staticmethod
    async def get_queue_stats_v2(
        namespace_data_access,
        namespace: str,
        queue: Optional[str] = None,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None
    ) -> Dict[str, Any]:
        conn = await namespace_data_access.manager.get_connection(namespace)
        
        redis_client = await conn.get_redis_client(decode=False)
        
        pg_session = None
        if conn.AsyncSessionLocal:
            pg_session = conn.AsyncSessionLocal()
        
        try:
            from jettask.webui.services.queue_stats_v2 import QueueStatsV2
            
            stats_service = QueueStatsV2(
                redis_client=redis_client,
                pg_session=pg_session,
                redis_prefix=conn.redis_prefix
            )
            
            time_filter = None
            if time_range or start_time or end_time:
                time_filter = {}
                
                if time_range and time_range != 'custom':
                    now = datetime.now(timezone.utc)
                    if time_range.endswith('m'):
                        minutes = int(time_range[:-1])
                        time_filter['start_time'] = now - timedelta(minutes=minutes)
                        time_filter['end_time'] = now
                    elif time_range.endswith('h'):
                        hours = int(time_range[:-1])
                        time_filter['start_time'] = now - timedelta(hours=hours)
                        time_filter['end_time'] = now
                    elif time_range.endswith('d'):
                        days = int(time_range[:-1])
                        time_filter['start_time'] = now - timedelta(days=days)
                        time_filter['end_time'] = now
                else:
                    if start_time:
                        time_filter['start_time'] = start_time
                    if end_time:
                        time_filter['end_time'] = end_time
            
            stats = await stats_service.get_queue_stats_grouped(time_filter)
            
            if queue:
                stats = [s for s in stats if s['queue_name'] == queue]
            
            return {
                "success": True,
                "data": stats
            }
            
        finally:
            if pg_session:
                await pg_session.close()
            await redis_client.aclose()
    
    @staticmethod
    async def get_tasks_v2(namespace_data_access, namespace: str, body: Dict[str, Any]) -> Dict[str, Any]:
        from sqlalchemy import text
        from datetime import datetime, timezone, timedelta
        
        queue_name = body.get('queue_name')
        page = body.get('page', 1)
        page_size = body.get('page_size', 20)
        filters = body.get('filters', [])
        time_range = body.get('time_range', '1h')
        start_time = body.get('start_time')
        end_time = body.get('end_time')
        sort_field = body.get('sort_field', 'created_at')
        sort_order = body.get('sort_order', 'desc')
        
        if not queue_name:
            raise ValueError("queue_name is required")
        
        conn = await namespace_data_access.manager.get_connection(namespace)
        
        if not conn.pg_config or not conn.async_engine:
            return {
                "success": True,
                "data": [],
                "total": 0
            }
        
        if start_time and end_time:
            start_dt = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
            end_dt = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
        else:
            end_dt = datetime.now(timezone.utc)
            time_deltas = {
                '15m': timedelta(minutes=15),
                '30m': timedelta(minutes=30),
                '1h': timedelta(hours=1),
                '3h': timedelta(hours=3),
                '6h': timedelta(hours=6),
                '12h': timedelta(hours=12),
                '1d': timedelta(days=1),
                '3d': timedelta(days=3),
                '7d': timedelta(days=7),
                '30d': timedelta(days=30)
            }
            delta = time_deltas.get(time_range, timedelta(hours=1))
            start_dt = end_dt - delta
        
        offset = (page - 1) * page_size
        
        async with conn.async_engine.begin() as pg_conn:
            conditions = [
                "t.namespace = :namespace",
                "t.queue = :queue",
                "t.created_at >= :start_time",
                "t.created_at <= :end_time"
            ]
            query_params = {
                "namespace": namespace,
                "queue": queue_name,
                "start_time": start_dt,
                "end_time": end_dt,
                "limit": page_size,
                "offset": offset
            }
            
            for i, filter_item in enumerate(filters):
                field = filter_item.get('field')
                operator = filter_item.get('operator')
                value = filter_item.get('value')
                
                if field and operator and value is not None:
                    param_key = f"filter_{i}"
                    
                    db_field_map = {
                        'id': 't.stream_id',
                        'task_name': "t.payload::jsonb->'event_data'->>'__task_name'",
                        'status': "t.payload::jsonb->>'status'",
                        'worker_id': "t.payload::jsonb->>'worker_id'",
                        'scheduled_task_id': 't.scheduled_task_id'
                    }
                    
                    db_field = db_field_map.get(field, f't.{field}')
                    
                    if operator == 'eq':
                        conditions.append(f"{db_field} = :{param_key}")
                        query_params[param_key] = value
                    elif operator == 'contains':
                        conditions.append(f"{db_field} LIKE :{param_key}")
                        query_params[param_key] = f"%{value}%"
                    elif operator == 'gt':
                        conditions.append(f"{db_field} > :{param_key}")
                        query_params[param_key] = value
                    elif operator == 'lt':
                        conditions.append(f"{db_field} < :{param_key}")
                        query_params[param_key] = value
            
            where_clause = " AND ".join(conditions)
            
            count_query = f"""
                SELECT COUNT(*) as total 
                FROM tasks t
                WHERE {where_clause}
            """
            count_result = await pg_conn.execute(text(count_query), query_params)
            total = count_result.fetchone().total
            
            sort_map = {
                'created_at': 't.created_at',
                'started_at': 't.started_at',
                'completed_at': 't.completed_at'
            }
            order_by = sort_map.get(sort_field, 't.created_at')
            order_direction = 'DESC' if sort_order == 'desc' else 'ASC'
            
            query = f"""
                SELECT 
                    t.stream_id as id,
                    t.payload::jsonb->'event_data'->>'__task_name' as task_name,
                    t.queue,
                    t.payload::jsonb->>'status' as status,
                    t.priority,
                    COALESCE((t.payload::jsonb->>'retry_count')::int, 0) as retry_count,
                    COALESCE((t.payload::jsonb->>'max_retry')::int, 3) as max_retry,
                    t.created_at,
                    (t.payload::jsonb->>'started_at')::timestamptz as started_at,
                    (t.payload::jsonb->>'completed_at')::timestamptz as completed_at,
                    t.payload::jsonb->>'worker_id' as worker_id,
                    t.payload::jsonb->>'error_message' as error_message,
                    (t.payload::jsonb->>'execution_time')::float as execution_time,
                    CASE 
                        WHEN t.payload::jsonb->>'completed_at' IS NOT NULL AND t.created_at IS NOT NULL 
                        THEN EXTRACT(EPOCH FROM ((t.payload::jsonb->>'completed_at')::timestamptz - t.created_at))
                        ELSE NULL 
                    END as duration,
                    t.scheduled_task_id,
                    t.source,
                    t.metadata
                FROM tasks t
                WHERE {where_clause}
                ORDER BY {order_by} {order_direction}
                LIMIT :limit OFFSET :offset
            """
            
            result = await pg_conn.execute(text(query), query_params)
            
            tasks = []
            for row in result:
                tasks.append({
                    "id": row.id,
                    "task_name": row.task_name or "unknown",
                    "queue": row.queue,
                    "status": row.status,
                    "priority": row.priority,
                    "retry_count": row.retry_count,
                    "max_retry": row.max_retry,
                    "created_at": row.created_at.isoformat() if row.created_at else None,
                    "started_at": row.started_at.isoformat() if row.started_at else None,
                    "completed_at": row.completed_at.isoformat() if row.completed_at else None,
                    "duration": round(row.duration, 2) if row.duration else None,
                    "execution_time": float(row.execution_time) if row.execution_time else None,
                    "worker_id": row.worker_id,
                    "error_message": row.error_message
                })
            
            return {
                "success": True,
                "data": tasks,
                "total": total
            }
    
    @staticmethod
    async def get_consumer_group_stats(namespace_data_access, namespace: str, group_name: str) -> Dict[str, Any]:
        conn = await namespace_data_access.manager.get_connection(namespace)
        
        if not conn.AsyncSessionLocal:
            raise ValueError("PostgreSQL not configured for this namespace")
        
        async with conn.AsyncSessionLocal() as session:
            query = text("""
                WITH group_stats AS (
                    SELECT 
                        tr.consumer_group,
                        tr.task_name,
                        COUNT(*) as total_tasks,
                        COUNT(CASE WHEN tr.status = 'success' THEN 1 END) as success_count,
                        COUNT(CASE WHEN tr.status = 'failed' THEN 1 END) as failed_count,
                        COUNT(CASE WHEN tr.status = 'running' THEN 1 END) as running_count,
                        AVG(tr.execution_time) as avg_execution_time,
                        MIN(tr.execution_time) as min_execution_time,
                        MAX(tr.execution_time) as max_execution_time,
                        AVG(tr.duration) as avg_duration,
                        MIN(tr.started_at) as first_task_time,
                        MAX(tr.completed_at) as last_task_time
                    FROM task_runs tr
                    WHERE tr.consumer_group = :group_name
                        AND tr.started_at > NOW() - INTERVAL '24 hours'
                    GROUP BY tr.consumer_group, tr.task_name
                ),
                hourly_stats AS (
                    SELECT 
                        DATE_TRUNC('hour', tr.started_at) as hour,
                        COUNT(*) as task_count,
                        AVG(tr.execution_time) as avg_exec_time
                    FROM task_runs tr
                    WHERE tr.consumer_group = :group_name
                        AND tr.started_at > NOW() - INTERVAL '24 hours'
                    GROUP BY DATE_TRUNC('hour', tr.started_at)
                    ORDER BY hour
                )
                SELECT 
                    (SELECT row_to_json(gs) FROM group_stats gs) as summary,
                    (SELECT json_agg(hs) FROM hourly_stats hs) as hourly_trend
            """)
            
            result = await session.execute(query, {'group_name': group_name})
            row = result.fetchone()
            
            if not row or not row.summary:
                return {
                    "success": True,
                    "data": {
                        "group_name": group_name,
                        "summary": {},
                        "hourly_trend": []
                    }
                }
            
            return {
                "success": True,
                "data": {
                    "group_name": group_name,
                    "summary": row.summary,
                    "hourly_trend": row.hourly_trend or []
                }
            }
    

    @classmethod
    async def get_queue_tasks_detail(
        cls,
        namespace: str,
        queue_name: str,
        pg_session: AsyncSession,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None
    ) -> Dict[str, Any]:
        try:
            time_range_result = cls._resolve_time_range(
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                default_range="15m"
            )
            start_time = time_range_result.start_time
            end_time = time_range_result.end_time

            logger.info(f"获取队列 tasks 详情: namespace={namespace}, queue={queue_name}, time_range={start_time} to {end_time}")

            task_stats_stmt = select(
                TaskRun.task_name,
                func.count().filter(TaskRun.status == 'success').label('success_count'),
                func.count().filter(TaskRun.status == 'failed').label('failed_count'),
                func.avg(TaskRun.duration).label('avg_duration')
            ).select_from(TaskRun).join(
                Task, TaskRun.stream_id == Task.stream_id
            ).where(
                and_(
                    Task.queue == queue_name,
                    Task.namespace == namespace,
                    TaskRun.created_at >= start_time,
                    TaskRun.created_at <= end_time,
                    TaskRun.task_name.isnot(None)
                )
            ).group_by(TaskRun.task_name)

            result = await pg_session.execute(task_stats_stmt)
            rows = result.all()

            tasks = []
            for row in rows:
                total_count = row.success_count + row.failed_count
                success_rate = round((row.success_count / total_count) * 100, 2) if total_count > 0 else 0.0

                tasks.append({
                    "task_name": row.task_name,
                    "success_count": row.success_count or 0,
                    "failed_count": row.failed_count or 0,
                    "success_rate": success_rate,
                    "avg_duration": round(row.avg_duration, 4) if row.avg_duration else 0.0
                })

            return {
                "success": True,
                "queue_name": queue_name,
                "tasks": tasks,
                "total": len(tasks),
                "time_range": {
                    "start_time": start_time.isoformat() if start_time else None,
                    "end_time": end_time.isoformat() if end_time else None
                }
            }

        except Exception as e:
            logger.error(f"获取队列 tasks 详情失败: queue={queue_name}, error={e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "queue_name": queue_name,
                "tasks": []
            }

    @classmethod
    async def _query_from_aggregation_table(
        cls,
        pg_session: AsyncSession,
        namespace: str,
        queues: List[str],
        start_time: datetime,
        end_time: datetime,
        interval_seconds: int
    ) -> List[Dict[str, Any]]:
        stmt = select(
            TaskMetricsMinute.queue,
            TaskMetricsMinute.time_bucket,
            TaskMetricsMinute.task_count
        ).where(
            and_(
                TaskMetricsMinute.namespace == namespace,
                TaskMetricsMinute.queue.in_(queues),
                TaskMetricsMinute.time_bucket >= start_time,
                TaskMetricsMinute.time_bucket <= end_time
            )
        ).order_by(
            TaskMetricsMinute.queue,
            TaskMetricsMinute.time_bucket
        )

        result = await pg_session.execute(stmt)
        rows = result.fetchall()

        time_buckets = []
        if interval_seconds > 60:
            start_timestamp = int(start_time.timestamp() / interval_seconds) * interval_seconds
            current = datetime.fromtimestamp(start_timestamp, tz=timezone.utc)
            end_timestamp = int(end_time.timestamp() / interval_seconds) * interval_seconds
            end_aligned = datetime.fromtimestamp(end_timestamp, tz=timezone.utc)
        else:
            current = start_time.replace(second=0, microsecond=0)
            end_aligned = end_time.replace(second=0, microsecond=0)

        while current <= end_aligned:
            time_buckets.append(current)
            current += timedelta(seconds=interval_seconds)

        queue_data_map = {}
        for queue_name in queues:
            queue_data_map[queue_name] = {bucket: 0 for bucket in time_buckets}

        for row in rows:
            queue_name = row.queue
            time_bucket = row.time_bucket
            count = row.task_count

            if interval_seconds > 60:
                bucket_timestamp = int(time_bucket.timestamp() / interval_seconds) * interval_seconds
                aligned_bucket = datetime.fromtimestamp(bucket_timestamp, tz=timezone.utc)

                if queue_name in queue_data_map and aligned_bucket in queue_data_map[queue_name]:
                    queue_data_map[queue_name][aligned_bucket] += count
            else:
                if queue_name in queue_data_map and time_bucket in queue_data_map[queue_name]:
                    queue_data_map[queue_name][time_bucket] = count

        data = []
        for queue_name in queues:
            trend = []
            for bucket in sorted(time_buckets):
                trend.append({
                    "time": bucket.isoformat(),
                    "count": queue_data_map[queue_name].get(bucket, 0)
                })

            data.append({
                "queue_name": queue_name,
                "trend": trend
            })

        return data

    @classmethod
    async def _query_from_tasks_table(
        cls,
        pg_session: AsyncSession,
        namespace: str,
        queues: List[str],
        start_time: datetime,
        end_time: datetime,
        interval_seconds: int
    ) -> List[Dict[str, Any]]:
        stmt = select(
            Task.queue,
            Task.created_at
        ).where(
            and_(
                Task.namespace == namespace,
                Task.queue.in_(queues),
                Task.created_at >= start_time,
                Task.created_at <= end_time
            )
        ).order_by(Task.created_at)

        result = await pg_session.execute(stmt)
        tasks = result.fetchall()

        time_buckets = []
        current = start_time
        while current <= end_time:
            time_buckets.append(current)
            current += timedelta(seconds=interval_seconds)

        queue_data_map = {}
        for queue_name in queues:
            queue_data_map[queue_name] = {bucket: 0 for bucket in time_buckets}

        for row in tasks:
            queue_name = row.queue
            created_at = row.created_at

            bucket_timestamp = int(created_at.timestamp() / interval_seconds) * interval_seconds
            bucket = datetime.fromtimestamp(bucket_timestamp, tz=timezone.utc)

            if queue_name in queue_data_map and bucket in queue_data_map[queue_name]:
                queue_data_map[queue_name][bucket] += 1

        data = []
        for queue_name in queues:
            trend = []
            for bucket in sorted(time_buckets):
                trend.append({
                    "time": bucket.isoformat(),
                    "count": queue_data_map[queue_name].get(bucket, 0)
                })

            data.append({
                "queue_name": queue_name,
                "trend": trend
            })

        return data


    @classmethod
    def _validate_where_clause(cls, where_clause: str) -> None:
        forbidden_keywords = [
            'DROP', 'DELETE', 'UPDATE', 'INSERT', 'TRUNCATE', 'ALTER', 'CREATE',
            'GRANT', 'REVOKE', 'EXEC', 'EXECUTE', 'UNION', 'INTO',
            'GROUP BY', 'ORDER BY', 'HAVING', 'LIMIT', 'OFFSET',
            '--', '/*', '*/', ';'
        ]
        upper_clause = where_clause.upper()
        for keyword in forbidden_keywords:
            if keyword in upper_clause:
                raise ValueError(f"WHERE 子句包含不允许的关键字: {keyword}")

    @classmethod
    def _resolve_ambiguous_columns(cls, where_clause: str) -> str:
        import re

        ambiguous_columns = {
            'stream_id': 'tasks',
            'created_at': 'tasks',  
            'namespace': 'tasks',   
        }

        result = where_clause

        for col, default_table in ambiguous_columns.items():
            pattern = rf'(?<![.\w])\b{col}\b(?!\s*\.)'

            if re.search(pattern, result, re.IGNORECASE):
                result = re.sub(
                    pattern,
                    f'{default_table}.{col}',
                    result,
                    flags=re.IGNORECASE
                )
                logger.debug(f"自动补全列名前缀: {col} -> {default_table}.{col}")

        return result

    @classmethod
    async def _build_task_runs_query_conditions(
        cls,
        namespace: str,
        registry,
        queue: str,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None,
        status: Optional[List[str]] = None,
        task_names: Optional[List[str]] = None,
        where_clause: Optional[str] = None
    ) -> QueryConditionsResult:
        from sqlalchemy import text

        time_range_result = cls._resolve_time_range(
            start_time=start_time,
            end_time=end_time,
            time_range=time_range,
            default_range="1h"
        )

        queue_result = await cls._resolve_queues([queue], registry, expand_priority=True)
        all_priority_queues = queue_result.all_priority_queues

        if where_clause and where_clause.strip():
            cls._validate_where_clause(where_clause)

        conditions = [
            Task.namespace == namespace,
            Task.queue.in_(all_priority_queues),
            Task.created_at >= time_range_result.start_time,
            Task.created_at <= time_range_result.end_time
        ]

        if status:
            conditions.append(TaskRun.status.in_(status))

        if task_names:
            conditions.append(TaskRun.task_name.in_(task_names))

        if where_clause and where_clause.strip():
            resolved_clause = cls._resolve_ambiguous_columns(where_clause.strip())
            conditions.append(text(resolved_clause))

        logger.info(
            f"构建任务查询条件: namespace={namespace}, queue={queue}, "
            f"priority_queues={all_priority_queues}, "
            f"time_range={time_range_result.start_time} to {time_range_result.end_time}, "
            f"status={status}, task_names={task_names}, where_clause={where_clause}"
        )

        return QueryConditionsResult(
            conditions=conditions,
            all_priority_queues=all_priority_queues,
            time_range_result=time_range_result
        )

    @classmethod
    async def get_task_runs_list(
        cls,
        namespace: str,
        pg_session: AsyncSession,
        registry,
        queue: str,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None,
        status: Optional[List[str]] = None,
        task_names: Optional[List[str]] = None,
        where_clause: Optional[str] = None,
        limit: int = 50,
        cursor: Optional[str] = None,
        sort_field: str = "created_at",
        sort_order: str = "desc",
        include_fields: Optional[List[str]] = None
    ) -> Dict[str, Any]:
        from sqlalchemy import desc, asc

        try:
            query_result = await cls._build_task_runs_query_conditions(
                namespace=namespace,
                registry=registry,
                queue=queue,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                status=status,
                task_names=task_names,
                where_clause=where_clause
            )

            conditions = query_result.conditions
            time_range_result = query_result.time_range_result

            if not query_result.all_priority_queues:
                return {
                    "success": True,
                    "data": [],
                    "queue": queue,
                    "time_range": {
                        "start_time": time_range_result.start_time.isoformat(),
                        "end_time": time_range_result.end_time.isoformat()
                    },
                    "pagination": {
                        "next_cursor": None,
                        "has_more": False
                    }
                }

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

            if cursor_data:
                cursor_stream_id = cursor_data.get("stream_id")
                if cursor_stream_id:
                    if sort_order.lower() == 'desc':
                        conditions.append(Task.stream_id < cursor_stream_id)
                    else:
                        conditions.append(Task.stream_id > cursor_stream_id)

            _large_field_map = {
                'payload': Task.payload,
                'result': TaskRun.result,
                'error': TaskRun.error,
                'metadata': Task.task_metadata,
            }
            _include = set(include_fields) if include_fields else set()

            select_columns = [
                Task.stream_id,
                Task.scheduled_task_id,
                Task.priority,
                Task.created_at,
                Task.trigger_time,
                Task.source,
                TaskRun.task_name,
                TaskRun.status,
                TaskRun.started_at,
                TaskRun.completed_at,
                TaskRun.retries,
                TaskRun.duration,
                TaskRun.consumer,
            ]
            for field_name in ('payload', 'result', 'error', 'metadata'):
                if field_name in _include:
                    select_columns.append(_large_field_map[field_name].label(field_name))

            query_stmt = select(*select_columns).select_from(
                Task
            ).outerjoin(
                TaskRun, Task.stream_id == TaskRun.stream_id
            ).where(and_(*conditions))

            sort_column = None
            if sort_field in ['stream_id', 'scheduled_task_id', 'priority', 'created_at', 'trigger_time', 'source']:
                sort_column = getattr(Task, sort_field, None)
            elif sort_field in ['task_name', 'status', 'started_at', 'completed_at', 'retries', 'duration', 'consumer']:
                sort_column = getattr(TaskRun, sort_field, None)

            if sort_column is not None:
                if sort_order.lower() == 'desc':
                    query_stmt = query_stmt.order_by(desc(sort_column))
                else:
                    query_stmt = query_stmt.order_by(asc(sort_column))
            else:
                query_stmt = query_stmt.order_by(desc(Task.created_at))

            query_stmt = query_stmt.limit(limit + 1)

            result = await pg_session.execute(query_stmt)
            rows = result.fetchall()

            has_more = len(rows) > limit
            if has_more:
                rows = rows[:limit]

            data = []
            last_stream_id = None
            for row in rows:
                delay = None
                if row.started_at and row.trigger_time:
                    delay = (row.started_at - row.trigger_time).total_seconds()

                record = {
                    "stream_id": row.stream_id,
                    "task_name": row.task_name,
                    "status": row.status,
                    "priority": row.priority,
                    "retries": row.retries or 0,
                    "created_at": row.created_at.isoformat() if row.created_at else None,
                    "started_at": row.started_at.isoformat() if row.started_at else None,
                    "completed_at": row.completed_at.isoformat() if row.completed_at else None,
                    "duration": float(row.duration) if row.duration else None,
                    "delay": round(delay, 3) if delay is not None else None,
                    "consumer": row.consumer,
                    "source": row.source,
                    "scheduled_task_id": row.scheduled_task_id
                }
                for field_name in ('payload', 'result', 'error', 'metadata'):
                    if field_name in _include:
                        record[field_name] = getattr(row, field_name, None)
                data.append(record)
                last_stream_id = row.stream_id

            next_cursor = None
            if has_more and last_stream_id:
                cursor_obj = {"stream_id": last_stream_id}
                next_cursor = base64.b64encode(json.dumps(cursor_obj).encode('utf-8')).decode('utf-8')

            result = {
                "success": True,
                "data": data,
                "queue": queue,
                "time_range": {
                    "start_time": time_range_result.start_time.isoformat(),
                    "end_time": time_range_result.end_time.isoformat()
                },
                "pagination": {
                    "next_cursor": next_cursor,
                    "has_more": has_more
                }
            }

            if not cursor:
                try:
                    count_stmt = select(func.count(Task.stream_id)).select_from(
                        Task
                    ).outerjoin(
                        TaskRun, Task.stream_id == TaskRun.stream_id
                    ).where(and_(*conditions))

                    count_result = await pg_session.execute(count_stmt)
                    estimated_total = count_result.scalar() or 0
                    result["pagination"]["estimated_total"] = estimated_total
                except Exception as count_err:
                    logger.warning(f"获取预估总数失败: {count_err}")

            return result

        except ValueError as e:
            logger.warning(f"参数错误: {e}")
            raise
        except Exception as e:
            logger.error(f"获取任务执行记录失败: {e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "data": [],
                "queue": queue,
                "pagination": {
                    "next_cursor": None,
                    "has_more": False
                }
            }

    TREND_GROUP_BY_COLUMNS = {
        'priority': Task.priority,
        'source': Task.source,
        'task_name': TaskRun.task_name,
        'consumer': TaskRun.consumer,
        'status': TaskRun.status,
    }

    @classmethod
    async def get_task_runs_trend(
        cls,
        namespace: str,
        pg_session: AsyncSession,
        registry,
        queue: str,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None,
        status: Optional[List[str]] = None,
        task_names: Optional[List[str]] = None,
        where_clause: Optional[str] = None,
        group_by: Optional[List[str]] = None
    ) -> Dict[str, Any]:
        from sqlalchemy import literal_column

        try:
            group_by_columns = []
            group_by_names = []
            if group_by:
                for dim in group_by:
                    if dim not in cls.TREND_GROUP_BY_COLUMNS:
                        raise ValueError(
                            f"无效的聚合维度: {dim}。"
                            f"支持: {', '.join(cls.TREND_GROUP_BY_COLUMNS.keys())}"
                        )
                    group_by_columns.append(cls.TREND_GROUP_BY_COLUMNS[dim])
                    group_by_names.append(dim)

            query_result = await cls._build_task_runs_query_conditions(
                namespace=namespace,
                registry=registry,
                queue=queue,
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                status=status,
                task_names=task_names,
                where_clause=where_clause
            )

            conditions = query_result.conditions
            time_range_result = query_result.time_range_result
            interval_seconds = time_range_result.interval_seconds

            if not query_result.all_priority_queues:
                return {
                    "success": True,
                    "data": {
                        "trend": [] if not group_by else {}
                    },
                    "queue": queue,
                    "group_by": group_by,
                    "granularity": time_range_result.granularity,
                    "time_range": {
                        "start_time": time_range_result.start_time.isoformat(),
                        "end_time": time_range_result.end_time.isoformat(),
                        "interval": time_range_result.interval,
                        "interval_seconds": interval_seconds
                    }
                }

            start_ts = time_range_result.start_time.timestamp()
            aligned_start_ts = (int(start_ts) // interval_seconds) * interval_seconds
            current = datetime.fromtimestamp(aligned_start_ts, tz=timezone.utc)

            time_buckets = []
            while current <= time_range_result.end_time:
                time_buckets.append(current)
                current += timedelta(seconds=interval_seconds)

            trunc_unit = 'minute' if interval_seconds < 3600 else 'hour'
            time_bucket_expr = func.date_trunc(trunc_unit, TaskRun.started_at)

            if group_by_columns:
                select_columns = [time_bucket_expr.label('time_bucket')]
                for i, col in enumerate(group_by_columns):
                    select_columns.append(col.label(f'group_{i}'))
                select_columns.extend([
                    func.count().label('total_count'),
                    func.count().filter(TaskRun.status == 'success').label('success_count'),
                    func.count().filter(TaskRun.status == 'failed').label('failed_count')
                ])

                group_by_exprs = [time_bucket_expr] + group_by_columns

                query_stmt = select(*select_columns).select_from(
                    Task
                ).outerjoin(
                    TaskRun, Task.stream_id == TaskRun.stream_id
                ).where(and_(*conditions)).group_by(
                    *group_by_exprs
                ).order_by(literal_column('time_bucket'))
            else:
                query_stmt = select(
                    time_bucket_expr.label('time_bucket'),
                    func.count().label('total_count'),
                    func.count().filter(TaskRun.status == 'success').label('success_count'),
                    func.count().filter(TaskRun.status == 'failed').label('failed_count')
                ).select_from(
                    Task
                ).outerjoin(
                    TaskRun, Task.stream_id == TaskRun.stream_id
                ).where(and_(*conditions)).group_by(
                    time_bucket_expr
                ).order_by(literal_column('time_bucket'))

            result = await pg_session.execute(query_stmt)
            rows = result.fetchall()

            if group_by_columns:
                grouped_data: Dict[str, Dict[datetime, Dict]] = {}

                for row in rows:
                    if row.time_bucket:
                        bucket_timestamp = int(row.time_bucket.timestamp() / interval_seconds) * interval_seconds
                        aligned_bucket = datetime.fromtimestamp(bucket_timestamp, tz=timezone.utc)

                        group_values = []
                        for i in range(len(group_by_columns)):
                            val = getattr(row, f'group_{i}', None)
                            group_values.append(str(val) if val is not None else "(unknown)")
                        group_key = "|".join(group_values)

                        if group_key not in grouped_data:
                            grouped_data[group_key] = {}

                        if aligned_bucket not in grouped_data[group_key]:
                            grouped_data[group_key][aligned_bucket] = {
                                "total_count": 0,
                                "success_count": 0,
                                "failed_count": 0
                            }

                        grouped_data[group_key][aligned_bucket]["total_count"] += row.total_count
                        grouped_data[group_key][aligned_bucket]["success_count"] += row.success_count
                        grouped_data[group_key][aligned_bucket]["failed_count"] += row.failed_count

                trend_by_group = {}
                for group_key, bucket_data in grouped_data.items():
                    group_values = group_key.split("|")
                    dimension_values = {}
                    for i, name in enumerate(group_by_names):
                        dimension_values[name] = group_values[i] if i < len(group_values) else "(unknown)"

                    trend = []
                    for bucket in time_buckets:
                        data_point = bucket_data.get(bucket, {
                            "total_count": 0,
                            "success_count": 0,
                            "failed_count": 0
                        })
                        trend.append({
                            "time": bucket.isoformat(),
                            "total_count": data_point["total_count"],
                            "success_count": data_point["success_count"],
                            "failed_count": data_point["failed_count"]
                        })

                    trend_by_group[group_key] = {
                        "dimensions": dimension_values,
                        "trend": trend
                    }

                return {
                    "success": True,
                    "data": trend_by_group,
                    "queue": queue,
                    "group_by": group_by,
                    "granularity": time_range_result.granularity,
                    "time_range": {
                        "start_time": time_range_result.start_time.isoformat(),
                        "end_time": time_range_result.end_time.isoformat(),
                        "interval": time_range_result.interval,
                        "interval_seconds": interval_seconds
                    }
                }
            else:
                bucket_data = {}
                for row in rows:
                    if row.time_bucket:
                        bucket_timestamp = int(row.time_bucket.timestamp() / interval_seconds) * interval_seconds
                        aligned_bucket = datetime.fromtimestamp(bucket_timestamp, tz=timezone.utc)

                        if aligned_bucket not in bucket_data:
                            bucket_data[aligned_bucket] = {
                                "total_count": 0,
                                "success_count": 0,
                                "failed_count": 0
                            }
                        bucket_data[aligned_bucket]["total_count"] += row.total_count
                        bucket_data[aligned_bucket]["success_count"] += row.success_count
                        bucket_data[aligned_bucket]["failed_count"] += row.failed_count

                trend = []
                for bucket in time_buckets:
                    data_point = bucket_data.get(bucket, {
                        "total_count": 0,
                        "success_count": 0,
                        "failed_count": 0
                    })
                    trend.append({
                        "time": bucket.isoformat(),
                        "total_count": data_point["total_count"],
                        "success_count": data_point["success_count"],
                        "failed_count": data_point["failed_count"]
                    })

                return {
                    "success": True,
                    "data": {
                        "trend": trend
                    },
                    "queue": queue,
                    "group_by": None,
                    "granularity": time_range_result.granularity,
                    "time_range": {
                        "start_time": time_range_result.start_time.isoformat(),
                        "end_time": time_range_result.end_time.isoformat(),
                        "interval": time_range_result.interval,
                        "interval_seconds": interval_seconds
                    }
                }

        except ValueError as e:
            logger.warning(f"参数错误: {e}")
            raise
        except Exception as e:
            logger.error(f"获取任务执行趋势失败: {e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "data": {"trend": []} if not group_by else {},
                "queue": queue
            }

    @classmethod
    async def get_task_run_detail(
        cls,
        namespace: str,
        pg_session: AsyncSession,
        stream_id: str
    ) -> Dict[str, Any]:
        try:
            query_stmt = select(
                Task.stream_id,
                Task.queue,
                Task.namespace,
                Task.scheduled_task_id,
                Task.payload,
                Task.priority,
                Task.delay,
                Task.created_at,
                Task.trigger_time,
                Task.source,
                Task.task_metadata,
                TaskRun.task_name,
                TaskRun.status,
                TaskRun.result,
                TaskRun.error,
                TaskRun.started_at,
                TaskRun.completed_at,
                TaskRun.retries,
                TaskRun.duration,
                TaskRun.consumer
            ).select_from(
                Task
            ).outerjoin(
                TaskRun, Task.stream_id == TaskRun.stream_id
            ).where(
                and_(
                    Task.namespace == namespace,
                    Task.stream_id == stream_id
                )
            )

            result = await pg_session.execute(query_stmt)
            row = result.fetchone()

            if not row:
                return {
                    "success": False,
                    "error": f"任务不存在: {stream_id}",
                    "data": None
                }

            delay_seconds = None
            if row.started_at and row.trigger_time:
                delay_seconds = (row.started_at - row.trigger_time).total_seconds()

            return {
                "success": True,
                "data": {
                    "stream_id": row.stream_id,
                    "queue": row.queue,
                    "namespace": row.namespace,
                    "scheduled_task_id": row.scheduled_task_id,
                    "payload": row.payload,
                    "priority": row.priority,
                    "delay": row.delay,
                    "created_at": row.created_at.isoformat() if row.created_at else None,
                    "trigger_time": row.trigger_time.isoformat() if row.trigger_time else None,
                    "source": row.source,
                    "metadata": row.task_metadata,
                    "task_name": row.task_name,
                    "status": row.status,
                    "result": row.result,
                    "error": row.error,
                    "started_at": row.started_at.isoformat() if row.started_at else None,
                    "completed_at": row.completed_at.isoformat() if row.completed_at else None,
                    "retries": row.retries or 0,
                    "duration": float(row.duration) if row.duration else None,
                    "delay_seconds": round(delay_seconds, 3) if delay_seconds is not None else None,
                    "consumer": row.consumer
                }
            }

        except Exception as e:
            logger.error(f"获取任务详情失败: stream_id={stream_id}, error={e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "data": None
            }


    @classmethod
    async def get_backlog_trend(
        cls,
        namespace: str,
        pg_session: AsyncSession,
        queues: List[str],
        task_names: Optional[List[str]] = None,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None,
        aggregate_by_queue: bool = False
    ) -> Dict[str, Any]:
        try:
            time_range_result = cls._resolve_time_range(
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                default_range="1h",
                min_interval_seconds=60
            )

            interval_seconds = time_range_result.interval_seconds

            conditions = [
                QueueSnapshot.namespace == namespace,
                QueueSnapshot.queue.in_(queues),
                QueueSnapshot.time_bucket >= time_range_result.start_time,
                QueueSnapshot.time_bucket <= time_range_result.end_time
            ]

            conditions.append(QueueSnapshot.task_name != '')

            if aggregate_by_queue:
                pass
            elif task_names:
                conditions.append(QueueSnapshot.task_name.in_(task_names))

            stmt = select(
                QueueSnapshot.time_bucket,
                QueueSnapshot.queue,
                QueueSnapshot.task_name,
                QueueSnapshot.backlog,
                QueueSnapshot.pending_read,
                QueueSnapshot.send_offset,
                QueueSnapshot.ack_offset,
                QueueSnapshot.read_offset
            ).where(
                and_(*conditions)
            ).order_by(
                QueueSnapshot.queue,
                QueueSnapshot.task_name,
                QueueSnapshot.time_bucket
            )

            result = await pg_session.execute(stmt)
            rows = result.fetchall()

            time_buckets = cls._generate_backlog_time_buckets(
                time_range_result.start_time,
                time_range_result.end_time,
                interval_seconds
            )

            if aggregate_by_queue:
                data = cls._aggregate_backlog_by_queue(rows, queues, time_buckets, interval_seconds)
            else:
                data = cls._aggregate_backlog_by_task(rows, queues, time_buckets, interval_seconds)

            return {
                "success": True,
                "data": data,
                "granularity": time_range_result.granularity,
                "time_range": {
                    "start_time": time_range_result.start_time.isoformat(),
                    "end_time": time_range_result.end_time.isoformat(),
                    "interval": time_range_result.interval,
                    "interval_seconds": interval_seconds
                }
            }

        except Exception as e:
            logger.error(f"获取积压趋势失败: {e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "data": []
            }

    @classmethod
    async def get_backlog_summary(
        cls,
        namespace: str,
        pg_session: AsyncSession,
        queues: List[str],
        task_names: Optional[List[str]] = None
    ) -> Dict[str, Any]:
        try:
            latest_time_stmt = select(
                func.max(QueueSnapshot.time_bucket)
            ).where(
                and_(
                    QueueSnapshot.namespace == namespace,
                    QueueSnapshot.queue.in_(queues)
                )
            )
            latest_result = await pg_session.execute(latest_time_stmt)
            latest_time = latest_result.scalar()

            if not latest_time:
                return {
                    "success": True,
                    "data": [],
                    "latest_time": None
                }

            conditions = [
                QueueSnapshot.namespace == namespace,
                QueueSnapshot.queue.in_(queues),
                QueueSnapshot.time_bucket == latest_time
            ]

            conditions.append(QueueSnapshot.task_name != '')

            if task_names:
                conditions.append(QueueSnapshot.task_name.in_(task_names))

            stmt = select(
                QueueSnapshot.queue,
                QueueSnapshot.task_name,
                QueueSnapshot.backlog,
                QueueSnapshot.pending_read,
                QueueSnapshot.send_offset,
                QueueSnapshot.ack_offset,
                QueueSnapshot.read_offset
            ).where(
                and_(*conditions)
            ).order_by(
                QueueSnapshot.queue,
                QueueSnapshot.task_name
            )

            result = await pg_session.execute(stmt)
            rows = result.fetchall()

            data = []
            for row in rows:
                data.append({
                    "queue": row.queue,
                    "task_name": row.task_name,
                    "backlog": row.backlog,
                    "pending_read": row.pending_read,
                    "send_offset": row.send_offset,
                    "ack_offset": row.ack_offset,
                    "read_offset": row.read_offset
                })

            return {
                "success": True,
                "data": data,
                "latest_time": latest_time.isoformat() if latest_time else None
            }

        except Exception as e:
            logger.error(f"获取积压汇总失败: {e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "data": []
            }

    @classmethod
    def _generate_backlog_time_buckets(
        cls,
        start_time: datetime,
        end_time: datetime,
        interval_seconds: int
    ) -> List[datetime]:
        current = start_time.replace(second=0, microsecond=0)
        buckets = []

        while current <= end_time:
            buckets.append(current)
            current += timedelta(seconds=interval_seconds)

        return buckets

    @classmethod
    def _aggregate_backlog_by_queue(
        cls,
        rows,
        queues: List[str],
        time_buckets: List[datetime],
        interval_seconds: int
    ) -> List[Dict[str, Any]]:
        queue_data = {q: {} for q in queues}

        for row in rows:
            queue = row.queue
            time_bucket = row.time_bucket
            aligned_bucket = cls._align_backlog_time_bucket(time_bucket, interval_seconds)

            if queue in queue_data:
                queue_data[queue][aligned_bucket] = {
                    "backlog": row.backlog,
                    "pending_read": row.pending_read
                }

        result = []
        for queue in queues:
            trend = []
            for bucket in time_buckets:
                data_point = queue_data.get(queue, {}).get(bucket, {
                    "backlog": 0,
                    "pending_read": 0
                })
                trend.append({
                    "time": bucket.isoformat(),
                    "backlog": data_point.get("backlog", 0),
                    "pending_read": data_point.get("pending_read", 0)
                })

            result.append({
                "queue": queue,
                "trend": trend
            })

        return result

    @classmethod
    def _aggregate_backlog_by_task(
        cls,
        rows,
        queues: List[str],
        time_buckets: List[datetime],
        interval_seconds: int
    ) -> List[Dict[str, Any]]:
        queue_task_data: Dict[str, Dict[str, Dict[datetime, Dict]]] = {}

        for row in rows:
            queue = row.queue
            task_name = row.task_name
            time_bucket = row.time_bucket
            aligned_bucket = cls._align_backlog_time_bucket(time_bucket, interval_seconds)

            if queue not in queue_task_data:
                queue_task_data[queue] = {}
            if task_name not in queue_task_data[queue]:
                queue_task_data[queue][task_name] = {}

            queue_task_data[queue][task_name][aligned_bucket] = {
                "backlog": row.backlog,
                "pending_read": row.pending_read
            }

        result = []
        for queue in queues:
            if queue not in queue_task_data:
                continue

            tasks = []
            for task_name, bucket_data in queue_task_data[queue].items():
                trend = []
                for bucket in time_buckets:
                    data_point = bucket_data.get(bucket, {
                        "backlog": 0,
                        "pending_read": 0
                    })
                    trend.append({
                        "time": bucket.isoformat(),
                        "backlog": data_point.get("backlog", 0),
                        "pending_read": data_point.get("pending_read", 0)
                    })

                tasks.append({
                    "task_name": task_name,
                    "trend": trend
                })

            result.append({
                "queue": queue,
                "tasks": tasks
            })

        return result

    @classmethod
    def _align_backlog_time_bucket(cls, time_bucket: datetime, interval_seconds: int) -> datetime:
        if interval_seconds <= 60:
            return time_bucket.replace(second=0, microsecond=0)

        timestamp = int(time_bucket.timestamp())
        aligned_timestamp = (timestamp // interval_seconds) * interval_seconds
        return datetime.fromtimestamp(aligned_timestamp, tz=timezone.utc)


    @classmethod
    async def get_worker_trend(
        cls,
        namespace: str,
        pg_session: AsyncSession,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None
    ) -> Dict[str, Any]:
        try:
            time_range_result = cls._resolve_time_range(
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                default_range="1h",
                min_interval_seconds=60
            )

            interval_seconds = time_range_result.interval_seconds

            conditions = [
                QueueSnapshot.namespace == namespace,
                QueueSnapshot.time_bucket >= time_range_result.start_time,
                QueueSnapshot.time_bucket <= time_range_result.end_time,
                QueueSnapshot.queue == '',
                QueueSnapshot.task_name == ''
            ]

            stmt = select(
                QueueSnapshot.time_bucket,
                QueueSnapshot.online_count,
                QueueSnapshot.offline_count,
                QueueSnapshot.total_count
            ).where(
                and_(*conditions)
            ).order_by(
                QueueSnapshot.time_bucket
            )

            result = await pg_session.execute(stmt)
            rows = result.fetchall()

            time_buckets = cls._generate_backlog_time_buckets(
                time_range_result.start_time,
                time_range_result.end_time,
                interval_seconds
            )

            bucket_data = {}
            for row in rows:
                time_bucket = row.time_bucket
                aligned_bucket = cls._align_backlog_time_bucket(time_bucket, interval_seconds)

                if aligned_bucket not in bucket_data:
                    bucket_data[aligned_bucket] = {
                        "online_count": 0,
                        "offline_count": 0,
                        "total_count": 0
                    }

                bucket_data[aligned_bucket] = {
                    "online_count": row.online_count,
                    "offline_count": row.offline_count,
                    "total_count": row.total_count
                }

            trend = []
            for bucket in time_buckets:
                data_point = bucket_data.get(bucket, {
                    "online_count": 0,
                    "offline_count": 0,
                    "total_count": 0
                })
                trend.append({
                    "time": bucket.isoformat(),
                    "online_count": data_point["online_count"],
                    "offline_count": data_point["offline_count"],
                    "total_count": data_point["total_count"]
                })

            return {
                "success": True,
                "data": {
                    "trend": trend
                },
                "granularity": time_range_result.granularity,
                "time_range": {
                    "start_time": time_range_result.start_time.isoformat(),
                    "end_time": time_range_result.end_time.isoformat(),
                    "interval": time_range_result.interval,
                    "interval_seconds": interval_seconds
                }
            }

        except Exception as e:
            logger.error(f"获取 Worker 趋势失败: {e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "data": {"trend": []}
            }

    @classmethod
    async def get_worker_trend_by_queue(
        cls,
        namespace: str,
        pg_session: AsyncSession,
        queue: str,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        time_range: Optional[str] = None
    ) -> Dict[str, Any]:
        try:
            time_range_result = cls._resolve_time_range(
                start_time=start_time,
                end_time=end_time,
                time_range=time_range,
                default_range="1h",
                min_interval_seconds=60
            )

            interval_seconds = time_range_result.interval_seconds

            conditions = [
                QueueSnapshot.namespace == namespace,
                QueueSnapshot.queue == queue,
                QueueSnapshot.task_name == '',
                QueueSnapshot.time_bucket >= time_range_result.start_time,
                QueueSnapshot.time_bucket <= time_range_result.end_time,
            ]

            stmt = select(
                QueueSnapshot.time_bucket,
                QueueSnapshot.online_count,
                QueueSnapshot.offline_count,
                QueueSnapshot.total_count
            ).where(
                and_(*conditions)
            ).order_by(
                QueueSnapshot.time_bucket
            )

            result = await pg_session.execute(stmt)
            rows = result.fetchall()

            time_buckets = cls._generate_backlog_time_buckets(
                time_range_result.start_time,
                time_range_result.end_time,
                interval_seconds
            )

            bucket_data = {}
            for row in rows:
                aligned_bucket = cls._align_backlog_time_bucket(row.time_bucket, interval_seconds)
                bucket_data[aligned_bucket] = {
                    "online_count": row.online_count,
                    "offline_count": row.offline_count,
                    "total_count": row.total_count
                }

            trend = []
            for bucket in time_buckets:
                data_point = bucket_data.get(bucket, {
                    "online_count": 0,
                    "offline_count": 0,
                    "total_count": 0
                })
                trend.append({
                    "time": bucket.isoformat(),
                    "online_count": data_point["online_count"],
                    "offline_count": data_point["offline_count"],
                    "total_count": data_point["total_count"]
                })

            return {
                "success": True,
                "data": {
                    "trend": trend
                },
                "queue": queue,
                "granularity": time_range_result.granularity,
                "time_range": {
                    "start_time": time_range_result.start_time.isoformat(),
                    "end_time": time_range_result.end_time.isoformat(),
                    "interval": time_range_result.interval,
                    "interval_seconds": interval_seconds
                }
            }

        except Exception as e:
            logger.error(f"获取队列级 Worker 趋势失败: queue={queue}, error={e}", exc_info=True)
            return {
                "success": False,
                "error": str(e),
                "data": {"trend": []}
            }
