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

/**
 * Debezium CDC 事件结构 (经过 ExtractNewRecordState 转换后)
 * 来自 referral-service 的 referral_relationships 表
 */
interface CdcReferralPayload {
  // 推荐关系表字段 (snake_case from PostgreSQL)
  relationship_id: string;
  user_id: string;
  account_sequence: string;
  referrer_id?: string | null;
  root_user_id?: string | null;
  my_referral_code: string;
  used_referral_code?: string | null;
  ancestor_path?: string[] | null;  // PostgreSQL bigint[] 作为字符串数组
  depth: number;
  direct_referral_count: number;
  active_direct_count: number;
  created_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
}

/**
 * Referral CDC 消费者服务
 *
 * 消费 Debezium 从 referral-service PostgreSQL 捕获的推荐关系变更
 *
 * Topic: cdc.referral.public.referral_relationships
 */
@Injectable()
export class ReferralCdcConsumerService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(ReferralCdcConsumerService.name);
  private kafka: Kafka;
  private consumer: Consumer;
  private isRunning = false;

  // CDC Topic
  private readonly cdcTopic = 'cdc.referral.public.referral_relationships';
  private readonly consumerGroup: string;

  constructor(
    private readonly configService: ConfigService,
    private readonly prisma: PrismaService,
  ) {
    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_REFERRAL_CDC_GROUP', 'admin-service-referral-cdc');

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

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

    this.logger.log(`[Referral-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('[Referral-CDC] Consumer already running');
      return;
    }

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

      await this.consumer.subscribe({
        topic: this.cdcTopic,
        fromBeginning: false,
      });

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

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

      this.isRunning = true;
      this.logger.log('[Referral-CDC] Consumer started successfully');
    } catch (error) {
      this.logger.error('[Referral-CDC] Failed to start consumer:', error);
    }
  }

  async stop(): Promise<void> {
    if (!this.isRunning) return;

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

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

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

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

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

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

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

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

      this.logger.log(
        `[Referral-CDC] ✓ Processed ${operation} for user: ${data.account_sequence}`
      );
    } catch (error) {
      this.logger.error(`[Referral-CDC] Failed to process message:`, error);
      throw error; // 让 KafkaJS 重试
    }
  }

  private async processCdcEvent(data: CdcReferralPayload): 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: CdcReferralPayload): Promise<void> {
    const ancestorPath = this.parseAncestorPath(data.ancestor_path);

    await this.prisma.referralQueryView.upsert({
      where: { id: BigInt(data.relationship_id) },
      create: {
        id: BigInt(data.relationship_id),
        userId: BigInt(data.user_id),
        accountSequence: data.account_sequence,
        referrerId: data.referrer_id ? BigInt(data.referrer_id) : null,
        rootUserId: data.root_user_id ? BigInt(data.root_user_id) : null,
        myReferralCode: data.my_referral_code,
        usedReferralCode: data.used_referral_code || null,
        ancestorPath,
        depth: data.depth,
        directReferralCount: data.direct_referral_count,
        activeDirectCount: data.active_direct_count,
        createdAt: new Date(data.created_at),
        syncedAt: new Date(),
      },
      update: {
        userId: BigInt(data.user_id),
        accountSequence: data.account_sequence,
        referrerId: data.referrer_id ? BigInt(data.referrer_id) : null,
        rootUserId: data.root_user_id ? BigInt(data.root_user_id) : null,
        myReferralCode: data.my_referral_code,
        usedReferralCode: data.used_referral_code || null,
        ancestorPath,
        depth: data.depth,
        directReferralCount: data.direct_referral_count,
        activeDirectCount: data.active_direct_count,
        syncedAt: new Date(),
      },
    });

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

  private async handleUpdate(data: CdcReferralPayload): Promise<void> {
    const id = BigInt(data.relationship_id);
    const exists = await this.prisma.referralQueryView.findUnique({
      where: { id },
      select: { id: true },
    });

    if (!exists) {
      await this.handleCreateOrSnapshot(data);
      return;
    }

    const ancestorPath = this.parseAncestorPath(data.ancestor_path);

    await this.prisma.referralQueryView.update({
      where: { id },
      data: {
        referrerId: data.referrer_id ? BigInt(data.referrer_id) : null,
        rootUserId: data.root_user_id ? BigInt(data.root_user_id) : null,
        usedReferralCode: data.used_referral_code || null,
        ancestorPath,
        depth: data.depth,
        directReferralCount: data.direct_referral_count,
        activeDirectCount: data.active_direct_count,
        syncedAt: new Date(),
      },
    });

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

  private async handleDelete(data: CdcReferralPayload): Promise<void> {
    const id = BigInt(data.relationship_id);

    try {
      await this.prisma.referralQueryView.delete({
        where: { id },
      });
      this.logger.log(`[Referral-CDC] Deleted referral: ${data.account_sequence}`);
    } catch {
      this.logger.warn(`[Referral-CDC] Referral not found for delete: ${data.account_sequence}`);
    }
  }

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

  private parseAncestorPath(path: string[] | null | undefined): bigint[] {
    if (!path || !Array.isArray(path)) return [];
    return path.map(id => BigInt(id));
  }

  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,
    };
  }
}
