/**
 * Deposit Event Consumer Service for Wallet Service
 *
 * Consumes deposit confirmed events from blockchain-service via Kafka.
 * Credits user wallets when deposits are confirmed on-chain.
 */

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

export const DEPOSIT_TOPICS = {
  BLOCKCHAIN_DEPOSITS: 'blockchain.deposits',
} as const;

export interface DepositConfirmedPayload {
  depositId: string;
  chainType: string;
  txHash: string;
  toAddress: string;
  amount: string;
  amountFormatted: string;
  confirmations: number;
  accountSequence: string;
  userId: string;
}

export type DepositEventHandler = (payload: DepositConfirmedPayload) => Promise<void>;

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

  private depositConfirmedHandler?: DepositEventHandler;

  constructor(private readonly configService: ConfigService) {}

  async onModuleInit() {
    const brokers = this.configService.get<string>('KAFKA_BROKERS')?.split(',') || ['localhost:9092'];
    const clientId = this.configService.get<string>('KAFKA_CLIENT_ID') || 'wallet-service';
    const groupId = 'wallet-service-deposit-events';

    this.logger.log(`[INIT] Deposit Event 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] Topics: ${Object.values(DEPOSIT_TOPICS).join(', ')}`);

    // 企业级重试配置：
    // - 初始重试间隔 1 秒
    // - 最大重试间隔 5 分钟
    // - 最多重试 15 次（指数退避约 2.5 小时）
    // - 乘数因子 2（每次重试间隔翻倍）
    this.kafka = new Kafka({
      clientId,
      brokers,
      logLevel: logLevel.WARN,
      retry: {
        initialRetryTime: 1000,        // 1 秒
        maxRetryTime: 300000,          // 最大 5 分钟
        retries: 15,                   // 最多 15 次
        multiplier: 2,                 // 指数退避因子
        restartOnFailure: async () => true,  // 失败后重启消费者
      },
    });

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

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

      await this.consumer.subscribe({
        topics: Object.values(DEPOSIT_TOPICS),
        fromBeginning: false,
      });
      this.logger.log(`[SUBSCRIBE] Subscribed to deposit topics`);

      await this.startConsuming();
    } catch (error) {
      this.logger.error(`[ERROR] Failed to connect Deposit Event consumer`, error);
    }
  }

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

  /**
   * Register handler for deposit confirmed events
   */
  onDepositConfirmed(handler: DepositEventHandler): void {
    this.depositConfirmedHandler = handler;
    this.logger.log(`[REGISTER] DepositConfirmed handler registered`);
  }

  private async startConsuming(): Promise<void> {
    await this.consumer.run({
      eachMessage: async ({ topic, partition, message }: EachMessagePayload) => {
        const offset = message.offset;
        this.logger.log(`[RECEIVE] Message received: topic=${topic}, partition=${partition}, offset=${offset}`);

        try {
          const value = message.value?.toString();
          if (!value) {
            this.logger.warn(`[RECEIVE] Empty message received on ${topic}`);
            return;
          }

          this.logger.debug(`[RECEIVE] Raw message: ${value.substring(0, 500)}...`);

          const parsed = JSON.parse(value);
          const eventType = parsed.eventType;
          const payload = parsed.payload || parsed;

          this.logger.log(`[RECEIVE] Event type: ${eventType}`);

          if (eventType === 'blockchain.deposit.confirmed') {
            this.logger.log(`[HANDLE] Processing DepositConfirmed event`);
            this.logger.log(`[HANDLE] depositId: ${payload.depositId}`);
            this.logger.log(`[HANDLE] chainType: ${payload.chainType}`);
            this.logger.log(`[HANDLE] txHash: ${payload.txHash}`);
            this.logger.log(`[HANDLE] amount: ${payload.amountFormatted}`);
            this.logger.log(`[HANDLE] accountSequence: ${payload.accountSequence}`);
            this.logger.log(`[HANDLE] userId: ${payload.userId}`);

            if (this.depositConfirmedHandler) {
              await this.depositConfirmedHandler(payload as DepositConfirmedPayload);
              this.logger.log(`[HANDLE] DepositConfirmed handler completed`);
            } else {
              this.logger.warn(`[HANDLE] No handler registered for DepositConfirmed`);
            }
          } else if (eventType === 'blockchain.deposit.detected') {
            // Log detected deposits but don't process them yet (wait for confirmation)
            this.logger.log(`[SKIP] DepositDetected event received, waiting for confirmation`);
            this.logger.log(`[SKIP] txHash: ${payload.txHash}, amount: ${payload.amountFormatted}`);
          } else {
            this.logger.warn(`[RECEIVE] Unknown event type: ${eventType}`);
          }
        } catch (error) {
          this.logger.error(`[ERROR] Error processing deposit event from ${topic}`, error);
          // Re-throw to trigger Kafka retry mechanism
          // This ensures messages are not marked as consumed until successfully processed
          throw error;
        }
      },
    });

    this.logger.log(`[START] Started consuming deposit events`);
  }
}
