/**
 * Withdrawal Event Consumer Service for Wallet Service
 *
 * Consumes withdrawal status events from blockchain-service via Kafka.
 * Updates withdrawal order status when transactions are confirmed or failed.
 */

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

export const WITHDRAWAL_TOPICS = {
  BLOCKCHAIN_EVENTS: 'blockchain.events',
} as const;

export interface WithdrawalConfirmedPayload {
  orderNo: string;
  accountSequence: string;
  userId: string;
  status: 'CONFIRMED';
  txHash: string;
  blockNumber?: number;
  chainType: string;
  toAddress: string;
  netAmount: number;
}

export interface WithdrawalFailedPayload {
  orderNo: string;
  accountSequence: string;
  userId: string;
  status: 'FAILED';
  error: string;
  chainType: string;
  toAddress: string;
  netAmount: number;
}

export interface SystemWithdrawalConfirmedPayload {
  orderNo: string;
  fromAccountSequence: string;
  fromAccountName: string;
  toAccountSequence: string;
  status: 'CONFIRMED';
  txHash: string;
  blockNumber?: number;
  chainType: string;
  toAddress: string;
  amount: string;
}

export interface SystemWithdrawalFailedPayload {
  orderNo: string;
  fromAccountSequence: string;
  fromAccountName: string;
  toAccountSequence: string;
  status: 'FAILED';
  error: string;
  chainType: string;
  toAddress: string;
  amount: string;
}

export type WithdrawalConfirmedHandler = (payload: WithdrawalConfirmedPayload) => Promise<void>;
export type WithdrawalFailedHandler = (payload: WithdrawalFailedPayload) => Promise<void>;
export type SystemWithdrawalConfirmedHandler = (payload: SystemWithdrawalConfirmedPayload) => Promise<void>;
export type SystemWithdrawalFailedHandler = (payload: SystemWithdrawalFailedPayload) => Promise<void>;

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

  private withdrawalConfirmedHandler?: WithdrawalConfirmedHandler;
  private withdrawalFailedHandler?: WithdrawalFailedHandler;
  private systemWithdrawalConfirmedHandler?: SystemWithdrawalConfirmedHandler;
  private systemWithdrawalFailedHandler?: SystemWithdrawalFailedHandler;

  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-withdrawal-events';

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

    // 企业级重试配置：指数退避，最多重试约 2.5 小时
    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 Withdrawal Event consumer...`);
      await this.consumer.connect();
      this.isConnected = true;
      this.logger.log(`[CONNECT] Withdrawal Event consumer connected successfully`);

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

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

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

  /**
   * Register handler for withdrawal confirmed events
   */
  onWithdrawalConfirmed(handler: WithdrawalConfirmedHandler): void {
    this.withdrawalConfirmedHandler = handler;
    this.logger.log(`[REGISTER] WithdrawalConfirmed handler registered`);
  }

  /**
   * Register handler for withdrawal failed events
   */
  onWithdrawalFailed(handler: WithdrawalFailedHandler): void {
    this.withdrawalFailedHandler = handler;
    this.logger.log(`[REGISTER] WithdrawalFailed handler registered`);
  }

  /**
   * Register handler for system withdrawal confirmed events
   */
  onSystemWithdrawalConfirmed(handler: SystemWithdrawalConfirmedHandler): void {
    this.systemWithdrawalConfirmedHandler = handler;
    this.logger.log(`[REGISTER] SystemWithdrawalConfirmed handler registered`);
  }

  /**
   * Register handler for system withdrawal failed events
   */
  onSystemWithdrawalFailed(handler: SystemWithdrawalFailedHandler): void {
    this.systemWithdrawalFailedHandler = handler;
    this.logger.log(`[REGISTER] SystemWithdrawalFailed 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.withdrawal.confirmed') {
            this.logger.log(`[HANDLE] Processing WithdrawalConfirmed event`);
            this.logger.log(`[HANDLE] orderNo: ${payload.orderNo}`);
            this.logger.log(`[HANDLE] txHash: ${payload.txHash}`);
            this.logger.log(`[HANDLE] blockNumber: ${payload.blockNumber}`);

            if (this.withdrawalConfirmedHandler) {
              await this.withdrawalConfirmedHandler(payload as WithdrawalConfirmedPayload);
              this.logger.log(`[HANDLE] WithdrawalConfirmed handler completed`);
            } else {
              this.logger.warn(`[HANDLE] No handler registered for WithdrawalConfirmed`);
            }
          } else if (eventType === 'blockchain.withdrawal.failed') {
            this.logger.log(`[HANDLE] Processing WithdrawalFailed event`);
            this.logger.log(`[HANDLE] orderNo: ${payload.orderNo}`);
            this.logger.log(`[HANDLE] error: ${payload.error}`);

            if (this.withdrawalFailedHandler) {
              await this.withdrawalFailedHandler(payload as WithdrawalFailedPayload);
              this.logger.log(`[HANDLE] WithdrawalFailed handler completed`);
            } else {
              this.logger.warn(`[HANDLE] No handler registered for WithdrawalFailed`);
            }
          } else if (eventType === 'blockchain.system-withdrawal.confirmed') {
            this.logger.log(`[HANDLE] Processing SystemWithdrawalConfirmed event`);
            this.logger.log(`[HANDLE] orderNo: ${payload.orderNo}`);
            this.logger.log(`[HANDLE] txHash: ${payload.txHash}`);
            this.logger.log(`[HANDLE] toAccountSequence: ${payload.toAccountSequence}`);

            if (this.systemWithdrawalConfirmedHandler) {
              await this.systemWithdrawalConfirmedHandler(payload as SystemWithdrawalConfirmedPayload);
              this.logger.log(`[HANDLE] SystemWithdrawalConfirmed handler completed`);
            } else {
              this.logger.warn(`[HANDLE] No handler registered for SystemWithdrawalConfirmed`);
            }
          } else if (eventType === 'blockchain.system-withdrawal.failed') {
            this.logger.log(`[HANDLE] Processing SystemWithdrawalFailed event`);
            this.logger.log(`[HANDLE] orderNo: ${payload.orderNo}`);
            this.logger.log(`[HANDLE] error: ${payload.error}`);

            if (this.systemWithdrawalFailedHandler) {
              await this.systemWithdrawalFailedHandler(payload as SystemWithdrawalFailedPayload);
              this.logger.log(`[HANDLE] SystemWithdrawalFailed handler completed`);
            } else {
              this.logger.warn(`[HANDLE] No handler registered for SystemWithdrawalFailed`);
            }
          } else if (eventType === 'blockchain.withdrawal.status' || eventType === 'blockchain.withdrawal.received') {
            // Log status updates but don't process them (informational only)
            this.logger.log(`[INFO] Withdrawal status update: ${payload.status} for ${payload.orderNo}`);
          } else {
            // Ignore other event types
            this.logger.debug(`[SKIP] Ignoring event type: ${eventType}`);
          }
        } catch (error) {
          this.logger.error(`[ERROR] Error processing withdrawal 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 withdrawal events`);
  }
}
