import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Prisma } from '@prisma/client';
import { Kafka, Consumer, EachMessagePayload } from 'kafkajs';
import { PrismaService } from '../persistence/prisma/prisma.service';

/** Prisma 事务客户端类型 */
export type TransactionClient = Prisma.TransactionClient;

/**
 * CDC 事件接口
 *
 * 注意：由于 Debezium 配置了 ExtractNewRecordState 转换（unwrap），
 * 消息格式是扁平化的，字段直接在根级别，元数据字段以 __ 为前缀。
 *
 * 扁平化格式示例：
 * {
 *   "order_id": 1,
 *   "tree_count": 1,
 *   "account_sequence": "D25122700015",
 *   "__op": "c",
 *   "__table": "planting_orders",
 *   "__source_ts_ms": 1767892060857,
 *   "__deleted": "false"
 * }
 */
export interface CDCEvent {
  // 为了兼容性，构造一个 payload 对象
  payload: {
    op: 'c' | 'u' | 'd' | 'r'; // create, update, delete, read (snapshot)
    before: any | null;
    after: any | null;
    table: string;
    source_ts_ms: number;
    deleted: boolean;
  };
  // Kafka 消息元数据
  topic: string;
  offset: bigint;
  // 内部使用：Kafka offset 作为序列号（向后兼容）
  sequenceNum: bigint;
}

/** 普通 handler（不支持事务） */
export type CDCHandler = (event: CDCEvent) => Promise<void>;

/** 事务性 handler（支持在事务中执行） */
export type TransactionalCDCHandler = (event: CDCEvent, tx: TransactionClient) => Promise<void>;

/** 事务性 handler 返回结果，用于事务提交后的回调 */
export type TransactionalCDCHandlerWithResult<T> = (event: CDCEvent, tx: TransactionClient) => Promise<T>;

/** 事务提交后的回调函数 */
export type PostCommitCallback<T> = (result: T) => Promise<void>;

/** Topic 消费阶段配置 */
export interface TopicPhase {
  topic: string;
  tableName: string;
}

/**
 * 收集的消息（用于排序后处理）
 * 包含原始 Kafka 消息和解析后的业务数据
 */
interface CollectedMessage {
  payload: EachMessagePayload;
  orderId: bigint;  // 用于排序的 order_id
}

@Injectable()
export class CDCConsumerService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(CDCConsumerService.name);
  private kafka: Kafka;
  private consumer: Consumer;
  private handlers: Map<string, CDCHandler> = new Map();
  private isRunning = false;

  // 分阶段消费配置
  private topicPhases: TopicPhase[] = [];
  private currentPhaseIndex = 0;
  private sequentialMode = false;

  // 初始同步完成标记（只有顺序同步全部完成后才为 true）
  private initialSyncCompleted = false;

  constructor(
    private readonly configService: ConfigService,
    private readonly prisma: PrismaService,
  ) {
    const brokers = this.configService.get<string>('KAFKA_BROKERS', 'localhost:9092').split(',');

    this.kafka = new Kafka({
      clientId: 'contribution-service-cdc',
      brokers,
    });

    this.consumer = this.kafka.consumer({
      groupId: 'contribution-service-cdc-group',
    });
  }

  async onModuleInit() {
    // 不在这里启动，等待注册处理器后再启动
  }

  async onModuleDestroy() {
    await this.stop();
  }

  /**
   * 事务性幂等包装器 - 100% 保证 exactly-once 语义
   *
   * 在同一个数据库事务中完成：
   * 1. 尝试插入幂等记录（使用唯一约束防止重复）
   * 2. 执行业务逻辑
   *
   * 任何步骤失败都会回滚整个事务，保证数据一致性
   */
  withIdempotency(handler: TransactionalCDCHandler): CDCHandler {
    return async (event: CDCEvent) => {
      const idempotencyKey = `${event.topic}:${event.offset}`;

      try {
        await this.prisma.$transaction(async (tx) => {
          // 1. 尝试插入幂等记录（使用唯一约束防止重复）
          try {
            await tx.processedCdcEvent.create({
              data: {
                sourceTopic: event.topic,
                offset: event.offset,
                tableName: event.payload.table,
                operation: event.payload.op,
              },
            });
          } catch (error: any) {
            // 唯一约束冲突 = 事件已处理，直接返回（不执行业务逻辑）
            if (error.code === 'P2002') {
              this.logger.debug(`[CDC] Skipping duplicate event: ${idempotencyKey}`);
              return;
            }
            throw error;
          }

          // 2. 执行业务逻辑（传入事务客户端）
          await handler(event, tx);

          this.logger.debug(`[CDC] Processed event in transaction: ${idempotencyKey}`);
        }, {
          // 设置事务隔离级别为 Serializable，防止并发问题
          isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
          timeout: 60000, // 60秒超时（算力计算可能需要较长时间）
        });
      } catch (error: any) {
        // 唯一约束冲突在事务外也可能发生（并发场景）
        if (error.code === 'P2002') {
          this.logger.debug(`[CDC] Skipping duplicate event (concurrent): ${idempotencyKey}`);
          return;
        }
        this.logger.error(`[CDC] Failed to process event: ${idempotencyKey}`, error);
        throw error;
      }
    };
  }

  /**
   * 事务性幂等包装器（带后置回调）- 100% 保证 exactly-once 语义
   *
   * 在同一个数据库事务中完成：
   * 1. 尝试插入幂等记录（使用唯一约束防止重复）
   * 2. 执行业务逻辑
   * 3. 事务提交后执行后置回调（如算力计算）
   *
   * 后置回调在事务提交后执行，失败不影响数据同步
   */
  withIdempotencyAndCallback<T>(
    handler: TransactionalCDCHandlerWithResult<T>,
    postCommitCallback?: PostCommitCallback<T>,
  ): CDCHandler {
    return async (event: CDCEvent) => {
      const idempotencyKey = `${event.topic}:${event.offset}`;
      let result: T | null = null;
      let shouldExecuteCallback = false;

      try {
        await this.prisma.$transaction(async (tx) => {
          // 1. 尝试插入幂等记录（使用唯一约束防止重复）
          try {
            await tx.processedCdcEvent.create({
              data: {
                sourceTopic: event.topic,
                offset: event.offset,
                tableName: event.payload.table,
                operation: event.payload.op,
              },
            });
          } catch (error: any) {
            // 唯一约束冲突 = 事件已处理，直接返回（不执行业务逻辑）
            if (error.code === 'P2002') {
              this.logger.debug(`[CDC] Skipping duplicate event: ${idempotencyKey}`);
              return;
            }
            throw error;
          }

          // 2. 执行业务逻辑（传入事务客户端）
          result = await handler(event, tx);
          shouldExecuteCallback = true;

          this.logger.debug(`[CDC] Processed event in transaction: ${idempotencyKey}`);
        }, {
          isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
          timeout: 60000,
        });

        // 3. 事务提交后执行后置回调
        if (shouldExecuteCallback && postCommitCallback && result !== null) {
          try {
            await postCommitCallback(result);
          } catch (callbackError) {
            // 后置回调失败不影响数据同步，只记录错误
            this.logger.error(`[CDC] Post-commit callback failed for: ${idempotencyKey}`, callbackError);
          }
        }
      } catch (error: any) {
        if (error.code === 'P2002') {
          this.logger.debug(`[CDC] Skipping duplicate event (concurrent): ${idempotencyKey}`);
          return;
        }
        this.logger.error(`[CDC] Failed to process event: ${idempotencyKey}`, error);
        throw error;
      }
    };
  }

  /**
   * 注册 CDC 事件处理器（普通模式，不保证幂等）
   * @deprecated 使用 registerTransactionalHandler 代替
   */
  registerHandler(tableName: string, handler: CDCHandler): void {
    this.handlers.set(tableName, handler);
    this.logger.log(`Registered CDC handler for table: ${tableName}`);
  }

  /**
   * 注册事务性 CDC 事件处理器（100% 幂等保证）
   * @param tableName 表名（如 "users", "adoptions", "referrals"）
   * @param handler 事务性处理函数
   */
  registerTransactionalHandler(tableName: string, handler: TransactionalCDCHandler): void {
    this.handlers.set(tableName, this.withIdempotency(handler));
    this.logger.log(`Registered transactional CDC handler for table: ${tableName}`);
  }

  /**
   * 注册事务性 CDC 事件处理器（带后置回调）
   * 适用于需要在事务提交后执行额外操作的场景（如算力计算）
   *
   * @param tableName 表名
   * @param handler 事务性处理函数，返回后置回调需要的数据
   * @param postCommitCallback 事务提交后执行的回调函数
   */
  registerTransactionalHandlerWithCallback<T>(
    tableName: string,
    handler: TransactionalCDCHandlerWithResult<T>,
    postCommitCallback: PostCommitCallback<T>,
  ): void {
    this.handlers.set(tableName, this.withIdempotencyAndCallback(handler, postCommitCallback));
    this.logger.log(`Registered transactional CDC handler with post-commit callback for table: ${tableName}`);
  }

  /**
   * 启动消费者（顺序模式）
   *
   * 按顺序消费三个 topic，确保数据依赖关系正确：
   * 1. 用户数据 (user_accounts)
   * 2. 推荐关系 (referral_relationships) - 依赖用户数据
   * 3. 认种订单 (planting_orders) - 依赖用户和推荐关系
   *
   * 每个阶段必须完全消费完毕后才进入下一阶段
   */
  async start(): Promise<void> {
    if (this.isRunning) {
      this.logger.warn('CDC consumer is already running');
      return;
    }

    try {
      await this.consumer.connect();
      this.logger.log('CDC consumer connected');

      // 配置顺序消费阶段（顺序很重要！）
      this.topicPhases = [
        {
          topic: this.configService.get<string>('CDC_TOPIC_USERS', 'cdc.identity.public.user_accounts'),
          tableName: 'user_accounts',
        },
        {
          topic: this.configService.get<string>('CDC_TOPIC_REFERRALS', 'cdc.referral.public.referral_relationships'),
          tableName: 'referral_relationships',
        },
        {
          topic: this.configService.get<string>('CDC_TOPIC_ADOPTIONS', 'cdc.planting.public.planting_orders'),
          tableName: 'planting_orders',
        },
      ];

      this.currentPhaseIndex = 0;
      this.sequentialMode = true;
      this.isRunning = true;

      // 开始顺序消费（阻塞直到完成，确保数据依赖顺序正确）
      await this.startSequentialConsumption();

      this.logger.log('CDC consumer started with sequential phase consumption');
    } catch (error) {
      this.logger.error('Failed to start CDC consumer', error);
      // 不抛出错误，允许服务在没有 Kafka 的情况下启动（用于本地开发）
    }
  }

  /**
   * 顺序消费所有阶段
   */
  private async startSequentialConsumption(): Promise<void> {
    for (let i = 0; i < this.topicPhases.length; i++) {
      this.currentPhaseIndex = i;
      const phase = this.topicPhases[i];

      this.logger.log(`[CDC] Starting phase ${i + 1}/${this.topicPhases.length}: ${phase.tableName} (${phase.topic})`);

      // 消费当前阶段直到追上最新
      await this.consumePhaseToEnd(phase);

      this.logger.log(`[CDC] Completed phase ${i + 1}/${this.topicPhases.length}: ${phase.tableName}`);
    }

    this.logger.log('[CDC] All phases completed. Switching to continuous mode...');

    // 所有阶段完成后，切换到持续消费模式（同时监听所有 topic）
    await this.startContinuousMode();
  }

  /**
   * 消费单个阶段直到追上最新消息
   *
   * 对于 planting_orders 阶段，使用"收集-排序-处理"模式：
   * 1. 先收集所有消息到内存
   * 2. 按 order_id（源系统主键）升序排序
   * 3. 再逐条处理
   *
   * 这解决了 Debezium snapshot 不按主键顺序读取导致的消息乱序问题，
   * 确保上游用户的认种记录先于下游用户处理，避免算力分配错误。
   */
  private async consumePhaseToEnd(phase: TopicPhase): Promise<void> {
    const admin = this.kafka.admin();
    await admin.connect();

    // 获取 topic 的高水位线和最早 offset
    const topicOffsets = await admin.fetchTopicOffsets(phase.topic);
    const highWatermarks: Map<number, string> = new Map();
    const earliestOffsets: Map<number, string> = new Map();

    for (const partitionOffset of topicOffsets) {
      highWatermarks.set(partitionOffset.partition, partitionOffset.high);
      earliestOffsets.set(partitionOffset.partition, partitionOffset.low);
    }

    this.logger.log(`[CDC] Phase ${phase.tableName}: High watermarks = ${JSON.stringify(Object.fromEntries(highWatermarks))}`);

    // 检查是否 topic 为空
    const allEmpty = Array.from(highWatermarks.values()).every(hw => hw === '0');
    if (allEmpty) {
      this.logger.log(`[CDC] Phase ${phase.tableName}: Topic is empty, skipping`);
      await admin.disconnect();
      return;
    }

    // 使用固定的 group id
    const phaseGroupId = `contribution-service-cdc-phase-${phase.tableName}`;

    // 重置 consumer group 的 offset 到最早位置
    // 使用 admin.resetOffsets 而不是 setOffsets，更简洁且专门用于重置到 earliest/latest
    // 这确保每次服务启动都会从头开始消费，不受之前 committed offset 影响
    // 参考: https://kafka.js.org/docs/admin#a-name-reset-offsets-a-resetoffsets
    this.logger.log(`[CDC] Phase ${phase.tableName}: Resetting consumer group ${phaseGroupId} offsets to earliest`);
    try {
      await admin.resetOffsets({
        groupId: phaseGroupId,
        topic: phase.topic,
        earliest: true,
      });
      this.logger.log(`[CDC] Phase ${phase.tableName}: Consumer group offsets reset successfully`);
    } catch (resetError: any) {
      // 如果 consumer group 不存在，resetOffsets 会失败，这是正常的（首次运行）
      // fromBeginning: true 会在这种情况下生效
      this.logger.log(`[CDC] Phase ${phase.tableName}: Could not reset offsets (may be first run): ${resetError.message}`);
    }

    const phaseConsumer = this.kafka.consumer({
      groupId: phaseGroupId,
    });

    try {
      await phaseConsumer.connect();

      // 订阅单个 topic，fromBeginning 对新 group 有效
      await phaseConsumer.subscribe({
        topic: phase.topic,
        fromBeginning: true,
      });

      // 判断是否需要使用"收集-排序-处理"模式
      const needsSorting = phase.tableName === 'planting_orders';

      if (needsSorting) {
        // planting_orders 阶段：使用"收集-排序-处理"模式
        await this.consumePhaseWithSorting(phaseConsumer, phase, highWatermarks);
      } else {
        // 其他阶段：使用原有的"边消费边处理"模式
        await this.consumePhaseDirectly(phaseConsumer, phase, highWatermarks);
      }

      // 停止消费
      await phaseConsumer.stop();
      await phaseConsumer.disconnect();
      await admin.disconnect();

    } catch (error) {
      this.logger.error(`[CDC] Error in phase ${phase.tableName}`, error);
      await phaseConsumer.disconnect();
      await admin.disconnect();
      throw error;
    }
  }

  /**
   * 直接消费模式（边消费边处理）
   * 用于 user_accounts 和 referral_relationships 阶段
   */
  private async consumePhaseDirectly(
    phaseConsumer: Consumer,
    phase: TopicPhase,
    highWatermarks: Map<number, string>,
  ): Promise<void> {
    let processedOffsets: Map<number, bigint> = new Map();
    let isComplete = false;

    for (const partition of highWatermarks.keys()) {
      processedOffsets.set(partition, BigInt(-1));
    }

    // 开始消费
    await phaseConsumer.run({
      eachMessage: async (payload: EachMessagePayload) => {
        await this.handleMessage(payload);

        // 更新已处理的 offset
        processedOffsets.set(payload.partition, BigInt(payload.message.offset));

        // 检查是否所有 partition 都已追上高水位线
        let allCaughtUp = true;
        for (const [partition, highWatermark] of highWatermarks) {
          const processed = processedOffsets.get(partition) ?? BigInt(-1);
          // 高水位线是下一个将被写入的 offset，所以已处理的 offset 需要 >= highWatermark - 1
          if (processed < BigInt(highWatermark) - BigInt(1)) {
            allCaughtUp = false;
            break;
          }
        }

        if (allCaughtUp && !isComplete) {
          isComplete = true;
          this.logger.log(`[CDC] Phase ${phase.tableName}: Caught up with all partitions`);
        }
      },
    });

    // 等待追上高水位线
    while (!isComplete) {
      await new Promise(resolve => setTimeout(resolve, 100));

      // 每秒检查一次进度
      const currentProgress = Array.from(processedOffsets.entries())
        .map(([p, o]) => `P${p}:${o}/${highWatermarks.get(p)}`)
        .join(', ');
      this.logger.debug(`[CDC] Phase ${phase.tableName} progress: ${currentProgress}`);
    }
  }

  /**
   * 收集-排序-处理模式
   * 用于 planting_orders 阶段，确保按 order_id（源系统主键）顺序处理
   *
   * 解决问题：Debezium snapshot 按 PostgreSQL 物理存储顺序读取数据，
   * 而非按主键顺序，导致下游用户的认种记录可能先于上游用户处理，
   * 造成算力分配错误（上游用户的 unlocked_level_depth 还未更新）。
   */
  private async consumePhaseWithSorting(
    phaseConsumer: Consumer,
    phase: TopicPhase,
    highWatermarks: Map<number, string>,
  ): Promise<void> {
    const collectedMessages: CollectedMessage[] = [];
    let processedOffsets: Map<number, bigint> = new Map();
    let isComplete = false;

    for (const partition of highWatermarks.keys()) {
      processedOffsets.set(partition, BigInt(-1));
    }

    this.logger.log(`[CDC] Phase ${phase.tableName}: Using collect-sort-process mode`);

    // 第一步：收集所有消息（不处理）
    await phaseConsumer.run({
      eachMessage: async (payload: EachMessagePayload) => {
        // 解析消息获取 order_id 用于排序
        const orderId = this.extractOrderIdFromPayload(payload);

        collectedMessages.push({
          payload,
          orderId,
        });

        // 更新已处理的 offset
        processedOffsets.set(payload.partition, BigInt(payload.message.offset));

        // 检查是否所有 partition 都已追上高水位线
        let allCaughtUp = true;
        for (const [partition, highWatermark] of highWatermarks) {
          const processed = processedOffsets.get(partition) ?? BigInt(-1);
          if (processed < BigInt(highWatermark) - BigInt(1)) {
            allCaughtUp = false;
            break;
          }
        }

        if (allCaughtUp && !isComplete) {
          isComplete = true;
          this.logger.log(`[CDC] Phase ${phase.tableName}: Collected all ${collectedMessages.length} messages`);
        }
      },
    });

    // 等待收集完成
    while (!isComplete) {
      await new Promise(resolve => setTimeout(resolve, 100));

      // 每秒检查一次进度
      const currentProgress = Array.from(processedOffsets.entries())
        .map(([p, o]) => `P${p}:${o}/${highWatermarks.get(p)}`)
        .join(', ');
      this.logger.debug(`[CDC] Phase ${phase.tableName} collecting: ${currentProgress}, collected: ${collectedMessages.length}`);
    }

    // 第二步：按 order_id 升序排序
    this.logger.log(`[CDC] Phase ${phase.tableName}: Sorting ${collectedMessages.length} messages by order_id`);
    collectedMessages.sort((a, b) => {
      if (a.orderId < b.orderId) return -1;
      if (a.orderId > b.orderId) return 1;
      return 0;
    });

    // 记录排序前后的变化（用于调试）
    if (collectedMessages.length > 0) {
      const firstFive = collectedMessages.slice(0, 5).map(m => m.orderId.toString()).join(', ');
      const lastFive = collectedMessages.slice(-5).map(m => m.orderId.toString()).join(', ');
      this.logger.log(`[CDC] Phase ${phase.tableName}: Sorted order_ids: first=[${firstFive}], last=[${lastFive}]`);
    }

    // 第三步：按排序后的顺序处理消息
    this.logger.log(`[CDC] Phase ${phase.tableName}: Processing ${collectedMessages.length} messages in sorted order`);
    let processedCount = 0;
    for (const collected of collectedMessages) {
      await this.handleMessage(collected.payload);
      processedCount++;

      // 每处理 100 条记录日志一次进度
      if (processedCount % 100 === 0) {
        this.logger.log(`[CDC] Phase ${phase.tableName}: Processed ${processedCount}/${collectedMessages.length} messages`);
      }
    }

    this.logger.log(`[CDC] Phase ${phase.tableName}: Completed processing all ${collectedMessages.length} messages in order_id order`);
  }

  /**
   * 从 Kafka 消息中提取 order_id（用于排序）
   */
  private extractOrderIdFromPayload(payload: EachMessagePayload): bigint {
    try {
      if (!payload.message.value) {
        return BigInt(0);
      }

      const rawData = JSON.parse(payload.message.value.toString());
      // order_id 是源表的主键字段
      const orderId = rawData.order_id || rawData.id || 0;

      // 转换为 bigint 用于比较
      return BigInt(orderId);
    } catch (error) {
      this.logger.warn(`[CDC] Failed to extract order_id from message, using 0`, error);
      return BigInt(0);
    }
  }

  /**
   * 切换到持续消费模式（所有 topic 同时消费）
   */
  private async startContinuousMode(): Promise<void> {
    this.sequentialMode = false;
    this.initialSyncCompleted = true;  // 标记初始同步完成

    const topics = this.topicPhases.map(p => p.topic);

    await this.consumer.subscribe({
      topics,
      fromBeginning: false, // 从上次消费的位置继续（不是从头开始）
    });

    this.logger.log(`[CDC] Continuous mode: Subscribed to topics: ${topics.join(', ')}`);

    await this.consumer.run({
      eachMessage: async (payload: EachMessagePayload) => {
        await this.handleMessage(payload);
      },
    });

    this.logger.log('[CDC] Continuous mode started - all topics being consumed in parallel');
  }

  /**
   * 获取 CDC 同步状态
   * - initialSyncCompleted = true: 初始顺序同步已完成
   */
  getSyncStatus(): { isRunning: boolean; sequentialMode: boolean; allPhasesCompleted: boolean } {
    return {
      isRunning: this.isRunning,
      sequentialMode: this.sequentialMode,
      allPhasesCompleted: this.initialSyncCompleted,
    };
  }

  /**
   * 停止消费者
   */
  async stop(): Promise<void> {
    if (!this.isRunning) {
      return;
    }

    try {
      await this.consumer.disconnect();
      this.isRunning = false;
      this.logger.log('CDC consumer stopped');
    } catch (error) {
      this.logger.error('Failed to stop CDC consumer', error);
    }
  }

  private async handleMessage(payload: EachMessagePayload): Promise<void> {
    const { topic, partition, message } = payload;

    try {
      if (!message.value) {
        this.logger.warn(`[CDC] Empty message received from topic ${topic}, partition ${partition}, offset ${message.offset}`);
        return;
      }

      const rawValue = message.value.toString();
      this.logger.debug(`[CDC] Raw message from ${topic}: ${rawValue.substring(0, 500)}${rawValue.length > 500 ? '...' : ''}`);

      const rawData = JSON.parse(rawValue);

      // Debezium ExtractNewRecordState 转换后的扁平化格式
      // 元数据字段: __op, __table, __source_ts_ms, __deleted
      // 数据字段直接在根级别
      const op = rawData.__op || rawData.op;
      const table = rawData.__table;
      const sourceTsMs = rawData.__source_ts_ms || 0;
      const deleted = rawData.__deleted === 'true' || rawData.__deleted === true;

      this.logger.log(`[CDC] Message parsed: topic=${topic}, op=${op}, table=${table}, offset=${message.offset}`);

      // 从原始数据中移除元数据字段，剩下的就是业务数据
      const { __op, __table, __source_ts_ms, __deleted, ...businessData } = rawData;

      const offset = BigInt(message.offset);

      // 构造兼容的 CDCEvent 对象
      // 对于 create/update/read，数据在 after；对于 delete，数据在 before
      const event: CDCEvent = {
        payload: {
          op: op as 'c' | 'u' | 'd' | 'r',
          before: op === 'd' ? businessData : null,
          after: op !== 'd' ? businessData : null,
          table: table,
          source_ts_ms: sourceTsMs,
          deleted: deleted,
        },
        topic,
        offset,
        sequenceNum: offset, // 向后兼容
      };

      // 从 topic 名称提取表名作为备选
      // 格式: cdc.<service>.public.<table_name>
      const parts = topic.split('.');
      const tableName = table || parts[parts.length - 1];

      this.logger.log(`[CDC] Dispatching to handler: tableName=${tableName}, op=${op}`);

      const handler = this.handlers.get(tableName);
      if (handler) {
        await handler(event);
        this.logger.log(`[CDC] Successfully processed event for table ${tableName}, op=${op}, offset=${message.offset}`);
      } else {
        this.logger.warn(`[CDC] No handler registered for table: ${tableName}. Available handlers: ${Array.from(this.handlers.keys()).join(', ')}`);
      }
    } catch (error) {
      this.logger.error(
        `[CDC] Error processing message from topic ${topic}, partition ${partition}, offset ${message.offset}`,
        error,
      );
      // 根据业务需求决定是否重试或记录到死信队列
    }
  }
}
