"""
Worker 查询服务（WebUI 侧）

从 Redis 实时读取 Worker 数据，提供 Worker 列表、汇总统计等查询能力。
"""
import json
import logging
import time
from typing import Dict, List, Optional, Set

logger = logging.getLogger(__name__)


class WorkerService:
    """Worker 查询服务"""

    HEARTBEAT_TIMEOUT = 15.0  

    def __init__(self, redis_client, redis_prefix: str):
        self.redis = redis_client
        self.redis_prefix = redis_prefix
        self.workers_registry_key = f"{redis_prefix}:REGISTRY:WORKERS"

    async def get_all_workers(self) -> List[Dict]:
        current_time = time.time()

        all_worker_ids = await self.redis.smembers(self.workers_registry_key)
        if not all_worker_ids:
            return []

        pipeline = self.redis.pipeline()
        worker_id_list = list(all_worker_ids)
        for worker_id in worker_id_list:
            wid = worker_id.decode('utf-8') if isinstance(worker_id, bytes) else worker_id
            worker_key = f"{self.redis_prefix}:WORKER:{wid}"
            pipeline.hgetall(worker_key)
        worker_data_list = await pipeline.execute()

        workers = []
        for worker_id, worker_data in zip(worker_id_list, worker_data_list):
            if not worker_data:
                continue

            wid = worker_id.decode('utf-8') if isinstance(worker_id, bytes) else worker_id
            worker_info = self._parse_worker_data(wid, worker_data, current_time)
            if worker_info:
                queues = worker_info.get('queues', [])
                if queues and all(
                    (q.get('task_name') or '').endswith('._handle_persist_task')
                    for q in queues
                ):
                    worker_info['worker_type'] = 'webui-consumer'
                else:
                    worker_info['worker_type'] = 'worker'
                workers.append(worker_info)

        workers.sort(key=lambda w: (
            0 if w.get('worker_type') == 'worker' else 1,
            0 if w.get('is_online') else 1,
        ))
        return workers

    async def get_workers_by_queue(self, queue: str) -> List[Dict]:
        all_workers = await self.get_all_workers()
        return [
            w for w in all_workers
            if any(q['queue'] == queue for q in w.get('queues', []))
        ]

    async def get_summary(self, workers: Optional[List[Dict]] = None) -> Dict:
        if workers is None:
            workers = await self.get_all_workers()

        online = sum(1 for w in workers if w.get('is_online'))
        offline = sum(1 for w in workers if not w.get('is_online'))
        return {
            "online_count": online,
            "offline_count": offline,
            "total_count": len(workers)
        }

    def _parse_worker_data(self, worker_id: str, data: dict, current_time: float) -> Optional[Dict]:

        def _get(key: str):
            val = data.get(key) or data.get(key.encode('utf-8') if isinstance(key, str) else key)
            if val is None:
                return None
            return val.decode('utf-8') if isinstance(val, bytes) else val

        consumer_id = _get('consumer_id') or worker_id
        host = _get('host') or ''
        pid_raw = _get('pid')
        pid = int(pid_raw) if pid_raw else None

        last_heartbeat_raw = _get('last_heartbeat')
        is_alive_raw = _get('is_alive')

        last_heartbeat = 0.0
        if last_heartbeat_raw:
            try:
                last_heartbeat = float(last_heartbeat_raw)
            except (ValueError, TypeError):
                pass

        is_alive = True
        if is_alive_raw:
            is_alive = str(is_alive_raw).lower() == 'true'

        is_online = is_alive and (current_time - last_heartbeat) < self.HEARTBEAT_TIMEOUT

        created_at_raw = _get('created_at')
        created_at = None
        if created_at_raw:
            try:
                created_at = float(created_at_raw)
            except (ValueError, TypeError):
                pass

        heartbeat_timeout_raw = _get('heartbeat_timeout')
        heartbeat_timeout = self.HEARTBEAT_TIMEOUT
        if heartbeat_timeout_raw:
            try:
                heartbeat_timeout = float(heartbeat_timeout_raw)
            except (ValueError, TypeError):
                pass

        messages_transferred_raw = _get('messages_transferred')
        messages_transferred = False
        if messages_transferred_raw:
            messages_transferred = str(messages_transferred_raw).lower() == 'true'

        uptime = ''
        if created_at and current_time > created_at:
            seconds = int(current_time - created_at)
            hours, remainder = divmod(seconds, 3600)
            minutes, _ = divmod(remainder, 60)
            if hours > 0:
                uptime = f"{hours}h {minutes}m"
            else:
                uptime = f"{minutes}m"

        queues = []
        for key, value in data.items():
            key_str = key.decode('utf-8') if isinstance(key, bytes) else key
            if not key_str.startswith('group_info:'):
                continue
            try:
                val_str = value.decode('utf-8') if isinstance(value, bytes) else value
                group_info = json.loads(val_str)
                queues.append({
                    "queue": group_info.get('queue', ''),
                    "task_name": group_info.get('task_name', ''),
                    "group_name": group_info.get('group_name', '')
                })
            except (json.JSONDecodeError, Exception) as e:
                logger.debug(f"解析 group_info 失败: {key_str}, error: {e}")

        return {
            "consumer_id": consumer_id,
            "host": host,
            "pid": pid,
            "is_online": is_online,
            "created_at": created_at,
            "last_heartbeat": last_heartbeat,
            "heartbeat_timeout": heartbeat_timeout,
            "messages_transferred": messages_transferred,
            "uptime": uptime,
            "queues": queues
        }
