/**
 * Adoption fUSDT Injection Event Consumer
 *
 * Subscribes to `cdc.contribution.outbox` topic (Debezium CDC)
 * Filters for `AdoptionFusdtInjectionRequested` events
 * Triggers fUSDT transfer from injection wallet to market maker wallet
 */

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

export interface AdoptionInjectionPayload {
  originalAdoptionId: string;
  accountSequence: string;
  treeCount: number;
  adoptionDate: string;
  amount: string;
  fusdtPerTree: number;
}

export type AdoptionInjectionEventHandler = (payload: AdoptionInjectionPayload) => Promise<void>;

@Injectable()
export class AdoptionInjectionConsumerService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(AdoptionInjectionConsumerService.name);
  private kafka: Kafka;
  private consumer: Consumer;
  private isConnected = false;
  private readonly topic: string;

  private injectionHandler?: AdoptionInjectionEventHandler;
  private enabled = false;

  constructor(private readonly configService: ConfigService) {
    this.topic = this.configService.get<string>(
      'CDC_TOPIC_CONTRIBUTION_OUTBOX',
      'cdc.contribution.outbox',
    );
  }

  isEnabled(): boolean {
    return this.enabled;
  }

  async onModuleInit() {
    // 未配置注入钱包时不启用消费者
    const walletUsername = this.configService.get<string>('FUSDT_INJECTION_WALLET_USERNAME');
    const walletAddress = this.configService.get<string>('FUSDT_INJECTION_WALLET_ADDRESS');
    if (!walletUsername || !walletAddress) {
      this.logger.warn(`[INIT] fUSDT 注入钱包未配置，Adoption Injection Consumer 不启用`);
      return;
    }

    const brokers = this.configService.get<string>('KAFKA_BROKERS')?.split(',') || ['localhost:9092'];
    const clientId = this.configService.get<string>('KAFKA_CLIENT_ID') || 'mining-blockchain-service';
    const groupId = 'mining-blockchain-adoption-injection';

    this.logger.log(`[INIT] Adoption Injection Consumer initializing...`);
    this.logger.log(`[INIT] ClientId: ${clientId}`);
    this.logger.log(`[INIT] GroupId: ${groupId}`);
    this.logger.log(`[INIT] Brokers: ${brokers.join(', ')}`);
    this.logger.log(`[INIT] Topic: ${this.topic}`);

    this.kafka = new Kafka({
      clientId,
      brokers,
      logLevel: logLevel.WARN,
      retry: {
        initialRetryTime: 1000,
        maxRetryTime: 300000,
        retries: 15,
        multiplier: 2,
        restartOnFailure: async () => true,
      },
    });

    this.consumer = this.kafka.consumer({
      groupId,
      sessionTimeout: 30000,
      heartbeatInterval: 3000,
    });

    try {
      this.logger.log(`[CONNECT] Connecting Adoption Injection consumer...`);
      await this.consumer.connect();
      this.isConnected = true;
      this.logger.log(`[CONNECT] Adoption Injection consumer connected successfully`);

      await this.consumer.subscribe({
        topics: [this.topic],
        fromBeginning: false,
      });
      this.logger.log(`[SUBSCRIBE] Subscribed to ${this.topic}`);

      await this.startConsuming();
      this.enabled = true;
    } catch (error) {
      this.logger.error(`[ERROR] Failed to connect Adoption Injection consumer`, error);
    }
  }

  async onModuleDestroy() {
    if (this.isConnected) {
      await this.consumer.disconnect();
      this.logger.log('Adoption Injection consumer disconnected');
    }
  }

  /**
   * Register handler for adoption injection events
   */
  onAdoptionInjectionRequested(handler: AdoptionInjectionEventHandler): void {
    this.injectionHandler = handler;
    this.logger.log(`[REGISTER] AdoptionInjectionRequested handler registered`);
  }

  private async startConsuming(): Promise<void> {
    await this.consumer.run({
      eachMessage: async ({ topic, partition, message }: EachMessagePayload) => {
        try {
          const value = message.value?.toString();
          if (!value) return;

          const parsed = JSON.parse(value);

          // Extract event_type from Debezium flattened outbox format
          // ExtractNewRecordState flattens the message to just the row data
          const eventType = parsed.event_type;

          // Only process AdoptionFusdtInjectionRequested events
          if (eventType !== 'AdoptionFusdtInjectionRequested') {
            return;
          }

          // Skip non-create operations (DELETE, UPDATE)
          const op = parsed.__op || parsed.op;
          if (op && op !== 'c' && op !== 'r') {
            return;
          }

          this.logger.log(`[RECEIVE] AdoptionFusdtInjectionRequested event received`);
          this.logger.log(`[RECEIVE] offset=${message.offset}, partition=${partition}`);

          // Parse the payload (JSON string from outbox_events table)
          let payload: AdoptionInjectionPayload;
          try {
            const rawPayload = typeof parsed.payload === 'string'
              ? JSON.parse(parsed.payload)
              : parsed.payload;
            payload = {
              originalAdoptionId: rawPayload.originalAdoptionId,
              accountSequence: rawPayload.accountSequence,
              treeCount: Number(rawPayload.treeCount),
              adoptionDate: rawPayload.adoptionDate,
              amount: rawPayload.amount,
              fusdtPerTree: Number(rawPayload.fusdtPerTree),
            };
          } catch (parseErr) {
            this.logger.error(`[ERROR] Failed to parse injection event payload`, parseErr);
            return;
          }

          this.logger.log(`[RECEIVE] adoptionId=${payload.originalAdoptionId}`);
          this.logger.log(`[RECEIVE] accountSequence=${payload.accountSequence}`);
          this.logger.log(`[RECEIVE] treeCount=${payload.treeCount}`);
          this.logger.log(`[RECEIVE] amount=${payload.amount} fUSDT`);

          if (this.injectionHandler) {
            await this.injectionHandler(payload);
            this.logger.log(`[HANDLE] AdoptionInjectionRequested handler completed`);
          } else {
            this.logger.warn(`[HANDLE] No handler registered for AdoptionInjectionRequested`);
          }
        } catch (error) {
          this.logger.error(`[ERROR] Error processing adoption injection event`, error);
        }
      },
    });

    this.logger.log(`[START] Started consuming adoption injection events from ${this.topic}`);
  }
}
