"""
简化的离线worker消息恢复模块
"""
import asyncio
import json
import logging
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
from redis.asyncio.lock import Lock as AsyncLock

import msgpack

if TYPE_CHECKING:
    from jettask.worker.lifecycle import WorkerManager

logger = logging.getLogger(__name__)


class OfflineWorkerRecovery:
    """离线worker消息恢复处理器"""

    def __init__(self, async_redis_client, redis_prefix='jettask', worker_prefix='WORKER', queue_formatter=None, queue_registry=None, worker_state: Optional['WorkerManager'] = None, app=None):
        """
        初始化离线worker消息恢复处理器

        Args:
            async_redis_client: 异步Redis客户端
            redis_prefix: Redis键前缀
            worker_prefix: Worker键前缀
            queue_formatter: 队列格式化函数
            queue_registry: 队列注册表
            worker_state: WorkerState实例（用于查询Worker状态）
            app: App实例（用于访问worker_state_manager和worker_id）
        """
        self.async_redis_client = async_redis_client
        self.app = app
        self.redis_prefix = redis_prefix
        self.worker_prefix = worker_prefix
        self._stop_recovery = False
        # 队列格式化函数，默认使用 prefix:QUEUE:queue_name 格式
        self.queue_formatter = queue_formatter or (lambda q: f"{self.redis_prefix}:QUEUE:{q}")
        # 通过 app 访问 worker_state_manager
        self.worker_state_manager = app.worker_state_manager if (app and hasattr(app, 'worker_state_manager')) else None
        # 队列注册表，用于获取优先级队列
        self.queue_registry = queue_registry
        # Worker状态查询器（必须传入）
        self._worker_state: Optional['WorkerState'] = worker_state
        
    async def recover_offline_workers(self,
                                     queue: str,
                                     task_name: str,
                                     current_consumer_name: str = None,
                                     event_queue: Optional[asyncio.Queue] = None,
                                     event_queue_callback: Optional[callable] = None) -> int:
        """
        恢复指定任务的离线worker的pending消息

        Args:
            queue: 队列名（可以是优先级队列）
            task_name: 任务名称
            current_consumer_name: 当前消费者名称
            event_queue: 事件队列
            event_queue_callback: 获取任务事件队列的回调函数

        Returns:
            恢复的消息数量
        """
        total_recovered = 0
        logger.debug(f'恢复任务 {task_name} 在队列 {queue} 的离线worker的pending消息')

        try:
            # 直接查找该任务的离线 worker
            offline_workers = await self._find_offline_workers(task_name)
            if not offline_workers:
                logger.info(f"No offline workers found for task {task_name}")
                return 0

            logger.info(f"Found {len(offline_workers)} offline workers for task {task_name}, starting recovery...")

            # 确定基础队列名（用于传递给恢复逻辑）
            base_queue = queue.split(':')[0]

            # 使用辅助方法恢复（只恢复传入的队列）
            total_recovered = await self._recover_queues_for_workers(
                [queue],  # 只恢复当前队列
                offline_workers,
                base_queue,
                current_consumer_name,
                event_queue,
                event_queue_callback
            )

        except Exception as e:
            logger.error(f"Error recovering offline workers for queue {queue}: {e}")
            import traceback
            traceback.print_exc()

        return total_recovered
        
    async def _find_offline_workers(self, task_name: str) -> List[Tuple[str, Dict]]:
        """查找指定任务的离线worker

        委托给 WorkerManager.find_offline_workers_for_task() 方法

        注意：worker_state 必须在 OfflineWorkerRecovery 初始化时传入
        """
        if self._worker_state is None:
            raise RuntimeError(
                "WorkerState not provided to OfflineWorkerRecovery. "
                "Please pass worker_state parameter during initialization."
            )

        return await self._worker_state.find_offline_workers_for_task(
            task_name=task_name,
            worker_prefix=self.worker_prefix
        )

    async def _recover_queues_for_workers(
        self,
        queues_to_recover: List[str],
        offline_workers: List[Tuple[str, Dict]],
        base_queue: str,
        current_consumer_name: str = None,
        event_queue: Optional[asyncio.Queue] = None,
        event_queue_callback: Optional[callable] = None
    ) -> int:
        """
        恢复一组队列的离线 worker 消息

        Args:
            queues_to_recover: 需要恢复的队列列表（包括优先级队列）
            offline_workers: 离线 worker 列表
            base_queue: 基础队列名
            其他参数同 recover_offline_workers_for_queue

        Returns:
            恢复的消息总数
        """
        total_recovered = 0

        # 🔧 获取当前 worker 的所有 group_name（用于匹配检查）
        current_worker_group_names = set()
        if self.app and self.app.worker_id:
            current_worker_key = f"{self.redis_prefix}:WORKER:{self.app.worker_id}"
            current_worker_data = await self.async_redis_client.hgetall(current_worker_key)

            for key, value in current_worker_data.items():
                if isinstance(key, bytes):
                    key = key.decode('utf-8')
                if isinstance(value, bytes):
                    value = value.decode('utf-8')

                if key.startswith('group_info:'):
                    try:
                        group_info = json.loads(value)
                        group_name = group_info.get('group_name')
                        if group_name:
                            current_worker_group_names.add(group_name)
                    except Exception as e:
                        logger.error(f"Error parsing current worker group_info: {e}")

            logger.info(f"[Recovery] 当前 worker 的 group_names: {current_worker_group_names}")
        else:
            logger.warning(f"[Recovery] 无法获取当前 worker 的 group_names (app.worker_id not available)")

        # 获取当前 worker 运行的任务名称集合
        current_task_names = set()
        if self.app and hasattr(self.app, '_tasks'):
            current_task_names = set(self.app._tasks.keys())
            logger.info(f"[Recovery] 当前 worker 运行的任务: {current_task_names}")
        else:
            logger.warning(f"[Recovery] 无法获取当前 worker 的任务列表，将恢复所有匹配队列的消息")

        # 对每个离线worker，提前提取 group_infos（使用任务名称匹配）
        workers_with_groups = []
        for worker_key, worker_data in offline_workers:
            group_infos = []
            for key, value in worker_data.items():
                if key.startswith('group_info:'):
                    try:
                        group_info = json.loads(value)
                        task_name = group_info.get('task_name')
                        group_queue = group_info.get('queue')

                        # 新的匹配逻辑：按任务名称匹配
                        # 1. 如果有当前任务列表，只恢复当前运行的任务的消息
                        # 2. 如果没有当前任务列表，则恢复所有消息（兼容旧逻辑）
                        if current_task_names:
                            if task_name in current_task_names:
                                group_infos.append(group_info)
                                logger.debug(f"Found group info for task {task_name} (task name match): {group_info}")
                            else:
                                logger.debug(f"Skipping group info for task {task_name} (not in current tasks)")
                        else:
                            # fallback: 如果无法获取当前任务列表，使用队列精确匹配逻辑
                            if group_queue == base_queue:
                                group_infos.append(group_info)
                                logger.debug(f"Found group info for base queue {base_queue} (exact match): {group_info}")
                    except Exception as e:
                        logger.error(f"Error parsing group_info: {e}")

            workers_with_groups.append((worker_key, worker_data, group_infos))
            logger.info(f"Worker {worker_key} has {len(group_infos)} groups (filtered by task names)")

        # 对每个队列（基础队列 + 优先级队列）恢复消息
        for queue_to_recover in queues_to_recover:
            if self._stop_recovery:
                logger.debug("Stopping recovery due to shutdown signal")
                break

            logger.info(f"Recovering queue: {queue_to_recover}")

            # 获取当前consumer名称
            consumer_name = current_consumer_name
            if not consumer_name and self.app and self.app.worker_id:
                # 🔧 修复：从当前worker的group_info中查找匹配的consumer_name
                # 而不是硬编码 {worker_id}-{base_queue}
                current_worker_key = f"{self.redis_prefix}:WORKER:{self.app.worker_id}"
                current_worker_data = await self.async_redis_client.hgetall(current_worker_key)

                # 查找匹配base_queue的group_info（精确匹配）
                found_consumer_name = None

                for key, value in current_worker_data.items():
                    if isinstance(key, bytes):
                        key = key.decode('utf-8')
                    if isinstance(value, bytes):
                        value = value.decode('utf-8')

                    if key.startswith('group_info:'):
                        try:
                            current_group_info = json.loads(value)
                            current_group_queue = current_group_info.get('queue')

                            # 精确匹配
                            if current_group_queue == base_queue:
                                found_consumer_name = current_group_info.get('consumer_name')
                                logger.debug(f"Found current consumer_name by exact match: {found_consumer_name}")
                                break
                        except Exception as e:
                            logger.error(f"Error parsing current group_info: {e}")

                if found_consumer_name:
                    consumer_name = found_consumer_name
                else:
                    # fallback：构造默认的consumer_name
                    consumer_name = f"{self.app.worker_id}-{base_queue}"
                    logger.warning(f"Could not find consumer_name in group_info, using fallback: {consumer_name}")

            if not consumer_name:
                logger.error(f"Cannot get current consumer name for queue {queue_to_recover}")
                continue

            logger.info(f"Starting recovery for queue {queue_to_recover} with consumer {consumer_name}")

            # 处理每个离线worker的这个队列的消息（传入预提取的 group_infos）
            for worker_key, worker_data, group_infos in workers_with_groups:
                if self._stop_recovery:
                    logger.debug("Stopping recovery due to shutdown signal")
                    break

                logger.info(f"Recovering messages from worker {worker_key} for queue {queue_to_recover}")
                recovered = await self._recover_worker_messages(
                    queue=queue_to_recover,
                    worker_key=worker_key,
                    worker_data=worker_data,
                    group_infos=group_infos,
                    current_consumer_name=consumer_name,
                    event_queue=event_queue,
                    event_queue_callback=event_queue_callback,
                    current_worker_group_names=current_worker_group_names  # 🔧 传递当前 worker 的 group_names
                )

                total_recovered += recovered

        return total_recovered

    async def _recover_worker_messages(self,
                                      queue: str,
                                      worker_key: str,
                                      worker_data: Dict,
                                      group_infos: List[Dict],
                                      current_consumer_name: str,
                                      event_queue: Optional[asyncio.Queue] = None,
                                      event_queue_callback: Optional[callable] = None,
                                      current_worker_group_names: Optional[set] = None) -> int:
        """
        恢复单个worker的pending消息

        Args:
            queue: 当前要恢复的队列（可能是基础队列或优先级队列）
            worker_key: Worker的Redis键
            worker_data: Worker的数据
            group_infos: 预提取的group_info列表（已按基础队列过滤）
            current_consumer_name: 当前consumer名称
            event_queue: 事件队列
            event_queue_callback: 获取任务事件队列的回调函数
            current_worker_group_names: 当前 worker 的所有 group_name 集合（用于匹配检查）

        Returns:
            恢复的消息数量
        """
        total_claimed = 0
        transferred = False
        # 提取 worker_id 用于分布式锁和标记
        worker_id = worker_key.split(':')[-1]

        # 🔧 使用分布式锁防止多个 worker 同时恢复同一个离线 worker 的消息
        recovery_lock_key = f"{self.redis_prefix}:RECOVERY:LOCK:{worker_id}:{queue}"
        recovery_lock = AsyncLock(
            self.async_redis_client,
            recovery_lock_key,
            timeout=60,  # 恢复可能需要较长时间
            blocking=False
        )

        if not await recovery_lock.acquire():
            logger.info(f"[Recovery] 恢复锁被占用，跳过: worker={worker_id}, queue={queue}")
            return 0

        try:
            # 🔧 检查是否已经转移过消息（避免重复恢复）
            messages_transferred = worker_data.get('messages_transferred', 'false')
            if isinstance(messages_transferred, bytes):
                messages_transferred = messages_transferred.decode('utf-8')

            if messages_transferred.lower() == 'true':
                logger.info(f"[Recovery] 消息已转移，跳过: worker={worker_id}, queue={queue}")
                return 0

            if not group_infos:
                logger.info(f"[Recovery] 没有 group_info，跳过: worker={worker_id}, queue={queue}")
                return 0

            # 处理每个group_info
            for group_info in group_infos:
                base_stream_key = group_info.get('stream_key')
                group_name = group_info.get('group_name')
                base_offline_consumer_name = group_info.get('consumer_name')
                task_name = group_info.get('task_name')
                base_queue = group_info.get('queue')

                if not all([base_stream_key, group_name, base_offline_consumer_name]):
                    logger.warning(f"Incomplete group_info: {group_info}")
                    continue

                # 🔧 检查当前 worker 是否有相同的 group_name
                if current_worker_group_names is not None and group_name not in current_worker_group_names:
                    logger.info(
                        f"[Recovery] 跳过恢复: group_name '{group_name}' 不在当前 worker 的 group_names 中 "
                        f"(worker: {worker_key}, task: {task_name})"
                    )
                    continue

                logger.info(f"[Recovery] ✓ group_name 匹配: {group_name}, 开始恢复消息")

                # 🔧 检查队列是否匹配：group_info 中的队列必须与当前要恢复的队列匹配
                # 直接比较完整队列名（包括优先级后缀）
                if base_queue != queue:
                    logger.debug(
                        f"[Recovery] 跳过恢复: 队列不匹配 "
                        f"(group_info queue={base_queue}, current queue={queue}, task={task_name})"
                    )
                    continue

                # 构建 stream_key 和 offline_consumer_name
                # group_info 中存储的是完整队列名（包括优先级后缀，如 robust_bench2:8）
                stream_key = f"{self.redis_prefix}:QUEUE:{queue}"
                offline_consumer_name = base_offline_consumer_name

                logger.info(f"Recovering task {task_name}: stream={stream_key}, group={group_name}, consumer={offline_consumer_name}")

                # 跳过自己的consumer，但只有在worker仍然活跃的情况下
                # 如果worker已经offline（is_alive=false或messages_transferred=true），即使consumer名称相同也应该恢复
                # 这处理了worker_id被复用的情况
                is_alive = worker_data.get('is_alive', 'false')
                if isinstance(is_alive, bytes):
                    is_alive = is_alive.decode('utf-8')
                is_alive = is_alive.lower() == 'true'

                messages_transferred = worker_data.get('messages_transferred', 'false')
                if isinstance(messages_transferred, bytes):
                    messages_transferred = messages_transferred.decode('utf-8')
                messages_transferred = messages_transferred.lower() == 'true'

                # 只有在worker活跃且消息未转移时，才跳过同名consumer
                if current_consumer_name == offline_consumer_name and is_alive and not messages_transferred:
                    logger.info(f"Skipping own active consumer: {offline_consumer_name}")
                    continue
                elif current_consumer_name == offline_consumer_name:
                    logger.info(f"Recovering same-name consumer from offline worker: {offline_consumer_name} (is_alive={is_alive}, messages_transferred={messages_transferred})")

                # 🔧 使用分布式锁保护单个 consumer group 的恢复（细粒度锁）
                # 这个锁与外层的 worker 级别锁不同，防止多个进程同时恢复同一个 consumer group
                lock_key = f"{self.redis_prefix}:CLAIM:LOCK:{offline_consumer_name}:{group_name}"
                group_lock = AsyncLock(
                    self.async_redis_client,
                    lock_key,
                    timeout=30,
                    blocking=False
                )

                if not await group_lock.acquire():
                    logger.info(f"[Recovery] consumer group 锁被占用，跳过: {offline_consumer_name}:{group_name}")
                    continue

                try:
                    # 🔧 优化：直接使用 xpending_range 获取 pending 消息（不需要先调用 xpending）
                    # 因为所有消息都通过 consumer group 消费，直接从 pending 列表恢复即可
                    batch_size = 100
                    total_claimed_count = 0

                    # 循环直到处理完所有消息
                    while True:
                        # 获取具体的pending消息信息（每次最多 batch_size 条）
                        detailed_pending = await self.async_redis_client.xpending_range(
                            stream_key, group_name,
                            min='-', max='+', count=batch_size,
                            consumername=offline_consumer_name
                        )

                        if not detailed_pending:
                            # 没有更多消息了
                            break

                        # 标记有消息被转移（即使最终没有成功 claim）
                        if not transferred:
                            transferred = True

                        logger.info(f"Found {len(detailed_pending)} pending messages for {task_name} (batch {total_claimed_count // batch_size + 1})")

                        # 批量认领消息
                        message_ids = [msg['message_id'] for msg in detailed_pending]
                        claimed_messages = await self.async_redis_client.xclaim(
                            stream_key, group_name,
                            current_consumer_name,
                            min_idle_time=0,
                            message_ids=message_ids
                        )

                        if claimed_messages:
                            logger.info(f"Claimed {len(claimed_messages)} messages for task {task_name} in this batch")
                            total_claimed_count += len(claimed_messages)

                            # 获取该任务的 event_queue
                            # 优先使用 event_queue_callback，其次使用直接传入的 event_queue
                            task_event_queue = None
                            if event_queue_callback and task_name:
                                task_event_queue = event_queue_callback(task_name)
                            elif event_queue:
                                task_event_queue = event_queue

                            # 如果有 event_queue，将消息放入队列
                            if task_event_queue:
                                logger.info(f'即将转移 {len(claimed_messages)=} 消息到 {task_name}')
                                for msg_id, msg_data in claimed_messages:
                                    if isinstance(msg_id, bytes):
                                        msg_id = msg_id.decode('utf-8')

                                    # 解析消息数据
                                    data_field = msg_data.get(b'data') or msg_data.get('data')
                                    if data_field:
                                        try:
                                            parsed_data = msgpack.unpackb(data_field, raw=False)
                                            # 添加必要的元数据
                                            parsed_data['_task_name'] = task_name
                                            parsed_data['queue'] = queue

                                            # 构建任务项
                                            task_item = {
                                                'queue': queue,
                                                'event_id': msg_id,
                                                'event_data': parsed_data,
                                                'consumer': current_consumer_name,
                                                'group_name': group_name
                                            }

                                            await task_event_queue.put(task_item)
                                            logger.debug(f"Put recovered message {msg_id} into event_queue for task {task_name}")
                                        except Exception as e:
                                            logger.error(f"Error processing claimed message: {e}")
                            else:
                                logger.warning(f"No event_queue available for task {task_name}, claimed messages will not be executed")

                            # 更新总计数
                            total_claimed += len(claimed_messages)

                            # 如果这批处理的消息数少于 batch_size，说明已经处理完了
                            if len(detailed_pending) < batch_size:
                                break
                        else:
                            # 没有成功 claim 到消息，退出循环
                            break

                        # 记录总恢复数量
                        if total_claimed_count > 0:
                            logger.info(f"Total claimed {total_claimed_count} messages for {task_name} from {offline_consumer_name}")
                finally:
                    await group_lock.release()

            # 🔧 所有消息恢复完成后，标记该 worker 的消息已转移
            if total_claimed > 0 or transferred:
                logger.info(f"[Recovery] 成功恢复 {total_claimed} 条消息，标记 worker {worker_id} 消息已转移")
                if self.worker_state_manager:
                    await self.worker_state_manager.mark_messages_transferred(worker_id, transferred=True)
                else:
                    await self.async_redis_client.hset(worker_key, 'messages_transferred', 'true')
            else:
                logger.info(f"[Recovery] 没有消息需要恢复: worker={worker_id}, queue={queue}")

        except Exception as e:
            import traceback
            traceback.print_exc()
            logger.error(f"[Recovery] 恢复消息时出错: {e}")

        finally:
            # 🔧 释放分布式锁
            await recovery_lock.release()

        return total_claimed
        
    async def _get_consumer_groups(self, stream_key: str, suffix: Optional[str] = None) -> List[str]:
        """获取Stream的consumer groups"""
        groups = []
        try:
            # 确保stream_key是字符串类型（如果是bytes会出问题）
            if isinstance(stream_key, bytes):
                stream_key = stream_key.decode('utf-8')
            
            all_groups = await self.async_redis_client.xinfo_groups(stream_key)
            logger.debug(f"Raw groups info for {stream_key}: {all_groups}")
            
            for group_info in all_groups:
                # 二进制Redis客户端返回的字典键是字符串，值是bytes
                group_name = group_info.get('name', b'')
                logger.debug(f"Processing group: {group_info}, group_name type: {type(group_name)}")
                
                # 解码group名称
                if isinstance(group_name, bytes):
                    group_name = group_name.decode('utf-8')
                
                # 过滤空的group名称
                if group_name:
                    if suffix:
                        if group_name.endswith(suffix):
                            groups.append(group_name)
                    else:
                        groups.append(group_name)
                        logger.debug(f"Added group: {group_name}")
        except Exception as e:
            logger.error(f"Error getting consumer groups for {stream_key}: {e}")
        return groups
        
    async def _claim_messages(self, stream_key: str, group_name: str, 
                             old_consumer: str, new_consumer: str) -> List[Tuple[bytes, Dict]]:
        """转移pending消息"""
        all_claimed = []
        last_id = '-'
        
        try:
            # 确保参数是bytes类型
            if isinstance(stream_key, str):
                stream_key = stream_key.encode('utf-8')
            if isinstance(group_name, str):
                group_name = group_name.encode('utf-8')
            
            logger.debug(f"_claim_messages: stream_key={stream_key}, group_name={group_name}, old_consumer={old_consumer}, new_consumer={new_consumer}")
                
            while True:
                # 获取pending消息
                pending_batch = await self.async_redis_client.xpending_range(
                    stream_key, group_name,
                    min=last_id, max='+',
                    count=100
                )
                
                logger.debug(f"Got {len(pending_batch) if pending_batch else 0} pending messages")
                
                if not pending_batch:
                    break
                
                # 过滤出属于旧consumer的消息
                message_ids = []
                for msg in pending_batch:
                    msg_consumer = msg.get('consumer') or msg.get(b'consumer')
                    if isinstance(msg_consumer, bytes):
                        msg_consumer = msg_consumer.decode('utf-8')
                    
                    if msg_consumer == old_consumer:
                        msg_id = msg.get('message_id') or msg.get(b'message_id')
                        message_ids.append(msg_id)
                
                if message_ids:
                    # 使用XCLAIM转移消息
                    logger.debug(f"Claiming {len(message_ids)} messages from {old_consumer} to {new_consumer}")
                    
                    claimed = await self.async_redis_client.xclaim(
                        stream_key, group_name,
                        new_consumer,
                        min_idle_time=0,
                        message_ids=message_ids,
                        force=True
                    )
                    
                    if claimed:
                        all_claimed.extend(claimed)
                
                # 更新游标
                if pending_batch:
                    last_msg_id = pending_batch[-1].get('message_id') or pending_batch[-1].get(b'message_id')
                    if isinstance(last_msg_id, bytes):
                        last_msg_id = last_msg_id.decode('utf-8')
                    # 增加ID以获取下一批
                    parts = last_msg_id.split('-')
                    if len(parts) == 2:
                        last_id = f"{parts[0]}-{int(parts[1]) + 1}"
                    else:
                        break
                else:
                    break
                    
        except Exception as e:
            logger.error(f"Error claiming messages: {e}")
            
        return all_claimed
        
    async def _put_to_event_queue(self, msg_id, msg_data, queue, event_queue, 
                                 consumer, group_name, old_consumer):
        """将转移的消息放入event_queue"""
        try:
            # 解析消息数据
            if b'data' in msg_data:
                event_data = msgpack.unpackb(msg_data[b'data'], raw=False)
            else:
                event_data = msg_data
            
            # 从 group_name 中提取 task_name
            # group_name 的格式是: "jettask:QUEUE:{queue}:{task_name}"
            task_name = None
            if group_name and ':' in group_name:
                parts = group_name.split(':')
                # 查找最后一个非数字部分作为task_name
                for i in range(len(parts) - 1, -1, -1):
                    part = parts[i]
                    # 跳过优先级数字
                    if not part.isdigit() and part not in ['jettask', 'QUEUE', queue]:
                        task_name = part
                        logger.debug(f"Extracted task_name '{task_name}' from group_name '{group_name}'")
                        break
            
            # 如果从group_name提取失败，尝试从consumer名称提取
            if not task_name and ':' in consumer and ':' in group_name:
                # consumer格式可能是: "{consumer_id}:{task_name}" 
                consumer_parts = consumer.split(':')
                if len(consumer_parts) > 1:
                    potential_task = consumer_parts[-1]
                    # 确保不是优先级数字
                    if not potential_task.isdigit():
                        task_name = potential_task
                        logger.debug(f"Extracted task_name '{task_name}' from consumer '{consumer}'")
            
            # 如果还是没有task_name，检查event_data中是否已有
            if not task_name and '_task_name' in event_data:
                task_name = event_data['_task_name']
                logger.debug(f"Using existing _task_name from event_data: '{task_name}'")
            
            # 确保event_data中有_task_name字段
            if task_name:
                event_data['_task_name'] = task_name
                logger.debug(f"Added _task_name '{task_name}' to recovered message")
            else:
                # 如果无法确定task_name，记录警告
                logger.warning(f"Could not determine task_name for recovered message. group_name='{group_name}', consumer='{consumer}'")
            
            # 构建事件
            event = {
                'event_id': msg_id.decode() if isinstance(msg_id, bytes) else msg_id,
                'event_data': event_data,
                'queue': queue,
                'consumer': consumer,
                'group_name': group_name,
                '_recovery': True,
                '_claimed_from': old_consumer
            }
            
            await event_queue.put(event)
            
        except Exception as e:
            logger.error(f"Error putting message to event queue: {e}")
            
    def stop(self):
        """停止恢复处理"""
        self._stop_recovery = True