import { Injectable, Logger, OnModuleInit, OnModuleDestroy, Inject } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Kafka, Consumer, logLevel, EachMessagePayload } from 'kafkajs';
import { PrismaService } from '../persistence/prisma/prisma.service';
import { IUserQueryRepository, USER_QUERY_REPOSITORY } from '../../domain/repositories/user-query.repository';

/**
 * Debezium CDC 事件结构 (经过 ExtractNewRecordState 转换后)
 *
 * 原始 Debezium 格式包含 before/after/op 等字段
 * 经过 unwrap 转换后，直接是表的字段 + __op, __table, __source_ts_ms
 */
interface CdcUserAccountPayload {
  // 用户表字段 (snake_case from PostgreSQL)
  user_id: string;
  account_sequence: string;
  phone_number?: string | null;
  nickname: string;
  avatar_url?: string | null;
  inviter_sequence?: string | null;
  kyc_status: string;
  status: string;
  registered_at: string;
  updated_at: string;

  // Debezium 元数据 (由 ExtractNewRecordState 添加)
  __op: 'c' | 'u' | 'd' | 'r';  // c=create, u=update, d=delete, r=read(snapshot)
  __table: string;
  __source_ts_ms: number;
  __deleted?: string;  // 'true' for delete events when using rewrite mode
}

/**
 * CDC 消费者服务
 *
 * 消费 Debezium 从 identity-service PostgreSQL 捕获的数据变更
 * 替代原有的基于 Outbox 的 UserEventConsumerService
 *
 * Topic: cdc.identity.public.user_accounts
 *
 * 优势：
 * - 零代码侵入 identity-service
 * - 100% 捕获所有数据变更
 * - 自动捕获 schema 变更
 */
@Injectable()
export class CdcConsumerService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(CdcConsumerService.name);
  private kafka: Kafka;
  private consumer: Consumer;
  private isRunning = false;

  // CDC Topic
  private readonly cdcTopic = 'cdc.identity.public.user_accounts';
  private readonly consumerGroup: string;

  constructor(
    private readonly configService: ConfigService,
    private readonly prisma: PrismaService,
    @Inject(USER_QUERY_REPOSITORY)
    private readonly userQueryRepository: IUserQueryRepository,
  ) {
    const brokers = (this.configService.get<string>('KAFKA_BROKERS', 'localhost:9092')).split(',');
    const clientId = this.configService.get<string>('KAFKA_CLIENT_ID', 'admin-service');
    this.consumerGroup = this.configService.get<string>('KAFKA_CDC_CONSUMER_GROUP', 'admin-service-cdc');

    this.kafka = new Kafka({
      clientId: `${clientId}-cdc`,
      brokers,
      logLevel: logLevel.WARN,
    });

    this.consumer = this.kafka.consumer({ groupId: this.consumerGroup });

    this.logger.log(`[CDC] Configured to consume topic: ${this.cdcTopic}`);
  }

  async onModuleInit() {
    await this.start();
  }

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

  async start(): Promise<void> {
    if (this.isRunning) {
      this.logger.warn('[CDC] Consumer already running');
      return;
    }

    try {
      this.logger.log('[CDC] Connecting to Kafka...');
      await this.consumer.connect();

      await this.consumer.subscribe({
        topic: this.cdcTopic,
        fromBeginning: false,  // 只消费新的变更
      });

      this.logger.log(`[CDC] Subscribed to topic: ${this.cdcTopic}`);

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

      this.isRunning = true;
      this.logger.log('[CDC] Consumer started successfully');
    } catch (error) {
      this.logger.error('[CDC] Failed to start consumer:', error);
      // 不阻塞服务启动，CDC 可以稍后重试
    }
  }

  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('[CDC] Failed to stop consumer:', error);
    }
  }

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

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

    try {
      const data = JSON.parse(message.value.toString()) as CdcUserAccountPayload;
      const operation = data.__op;
      const sourceTs = data.__source_ts_ms;

      this.logger.debug(
        `[CDC] Received ${operation} event for user ${data.account_sequence} ` +
        `(ts: ${new Date(sourceTs).toISOString()})`
      );

      // 幂等性检查：基于 message offset
      const eventId = `cdc:${topic}:${partition}:${message.offset}`;
      if (await this.isEventProcessed(eventId)) {
        this.logger.debug(`[CDC] Event ${eventId} already processed, skipping`);
        return;
      }

      // 处理 CDC 事件
      await this.processCdcEvent(data);

      // 记录已处理
      await this.markEventProcessed(eventId, `cdc:${operation}`);

      this.logger.log(
        `[CDC] ✓ Processed ${operation} for user: ${data.account_sequence}`
      );
    } catch (error) {
      this.logger.error(`[CDC] Failed to process message:`, error);
      // 不抛出错误，避免阻塞消费
    }
  }

  private async processCdcEvent(data: CdcUserAccountPayload): Promise<void> {
    const operation = data.__op;
    const isDeleted = data.__deleted === 'true';

    if (operation === 'd' || isDeleted) {
      // 删除操作
      await this.handleDelete(data);
    } else if (operation === 'c' || operation === 'r') {
      // 创建或快照读取
      await this.handleCreateOrSnapshot(data);
    } else if (operation === 'u') {
      // 更新操作
      await this.handleUpdate(data);
    }
  }

  private async handleCreateOrSnapshot(data: CdcUserAccountPayload): Promise<void> {
    const phoneNumberMasked = data.phone_number
      ? this.maskPhoneNumber(data.phone_number)
      : null;

    await this.userQueryRepository.upsert({
      userId: BigInt(data.user_id),
      accountSequence: data.account_sequence,
      nickname: data.nickname || null,
      avatarUrl: data.avatar_url || null,
      phoneNumberMasked,
      inviterSequence: data.inviter_sequence || null,
      kycStatus: data.kyc_status,
      status: data.status,
      registeredAt: new Date(data.registered_at),
    });

    this.logger.log(`[CDC] Created/Snapshot user: ${data.account_sequence}`);
  }

  private async handleUpdate(data: CdcUserAccountPayload): Promise<void> {
    // 使用 accountSequence 检查用户是否存在（而不是 userId）
    // 因为旧系统可能写入了 userId=0 的数据
    const exists = await this.userQueryRepository.existsByAccountSequence(data.account_sequence);

    if (!exists) {
      // 如果不存在，创建（可能是之前遗漏的）
      await this.handleCreateOrSnapshot(data);
      return;
    }

    const phoneNumberMasked = data.phone_number
      ? this.maskPhoneNumber(data.phone_number)
      : null;

    // 使用 accountSequence 作为条件更新，同时修复 userId
    await this.prisma.userQueryView.update({
      where: { accountSequence: data.account_sequence },
      data: {
        userId: BigInt(data.user_id),  // 修复可能错误的 userId
        nickname: data.nickname || null,
        avatarUrl: data.avatar_url || null,
        phoneNumberMasked,
        inviterSequence: data.inviter_sequence || null,
        kycStatus: data.kyc_status,
        status: data.status,
        syncedAt: new Date(),
      },
    });

    this.logger.log(`[CDC] Updated user: ${data.account_sequence}`);
  }

  private async handleDelete(data: CdcUserAccountPayload): Promise<void> {
    const userId = BigInt(data.user_id);

    // 软删除：更新状态为 DELETED
    const exists = await this.userQueryRepository.exists(userId);
    if (exists) {
      await this.userQueryRepository.updateStatus(userId, 'DELETED');
      this.logger.log(`[CDC] Marked user as deleted: ${data.account_sequence}`);
    }
  }

  // ==================== Helper Methods ====================

  private maskPhoneNumber(phone: string): string {
    if (phone.length < 7) return phone;
    return phone.slice(0, 3) + '****' + phone.slice(-4);
  }

  private async isEventProcessed(eventId: string): Promise<boolean> {
    const count = await this.prisma.processedEvent.count({
      where: { eventId },
    });
    return count > 0;
  }

  private async markEventProcessed(eventId: string, eventType: string): Promise<void> {
    await this.prisma.processedEvent.create({
      data: {
        eventId,
        eventType,
        processedAt: new Date(),
      },
    });
  }

  /**
   * 获取消费者状态
   */
  getStatus(): { isRunning: boolean; topic: string; consumerGroup: string } {
    return {
      isRunning: this.isRunning,
      topic: this.cdcTopic,
      consumerGroup: this.consumerGroup,
    };
  }
}
