/**
 * Kafka Event Publisher Service for Wallet Service
 *
 * Publishes domain events to Kafka for cross-service communication.
 * Used to notify blockchain-service about withdrawal requests.
 */

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

export interface EventPayload {
  eventId?: string;
  eventType: string;
  occurredAt?: Date;
  payload: { [key: string]: unknown };
}

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

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

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

    this.kafka = new Kafka({
      clientId,
      brokers,
      logLevel: logLevel.WARN,
      retry: {
        initialRetryTime: 100,
        retries: 8,
      },
    });

    this.producer = this.kafka.producer();

    try {
      await this.producer.connect();
      this.isConnected = true;
      this.logger.log(`[CONNECT] Kafka producer connected successfully`);
    } catch (error) {
      this.logger.error(`[ERROR] Failed to connect Kafka producer`, error);
    }
  }

  async onModuleDestroy() {
    if (this.isConnected) {
      await this.producer.disconnect();
      this.logger.log('Kafka producer disconnected');
    }
  }

  /**
   * Publish an event to Kafka
   */
  async publish(event: EventPayload): Promise<void> {
    if (!this.isConnected) {
      this.logger.warn(`[PUBLISH] Kafka not connected, skipping event: ${event.eventType}`);
      return;
    }

    const topic = this.getTopicForEvent(event.eventType);
    const eventId = event.eventId || this.generateEventId();
    const occurredAt = event.occurredAt || new Date();

    const message = {
      key: eventId,
      value: JSON.stringify({
        eventId,
        eventType: event.eventType,
        occurredAt: occurredAt.toISOString(),
        payload: event.payload,
      }),
      headers: {
        eventType: event.eventType,
        source: 'wallet-service',
      },
    };

    try {
      await this.producer.send({
        topic,
        messages: [message],
      });

      this.logger.log(`[PUBLISH] Published event: ${event.eventType} to topic: ${topic}`);
    } catch (error) {
      this.logger.error(`[ERROR] Failed to publish event: ${event.eventType}`, error);
      throw error;
    }
  }

  private getTopicForEvent(eventType: string): string {
    const topicMap: Record<string, string> = {
      'wallet.withdrawal.requested': 'wallet.withdrawals',
      'wallet.withdrawal.completed': 'wallet.withdrawals',
      'wallet.withdrawal.failed': 'wallet.withdrawals',
      // ACK events - 确认消息
      'wallet.deposit.credited': 'wallet.acks',
    };
    return topicMap[eventType] || 'wallet.events';
  }

  private generateEventId(): string {
    return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
  }
}
