/**
 * Event Consumer Service
 *
 * Consumes domain events from Kafka for async processing.
 * Handles keygen and signing requests from identity-service and blockchain-service.
 */

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

// Kafka Topics for consuming
export const MPC_CONSUME_TOPICS = {
  KEYGEN_REQUESTED: 'mpc.KeygenRequested',
  SIGNING_REQUESTED: 'mpc.SigningRequested',
} as const;

export interface KeygenRequestedPayload {
  sessionId: string;
  userId: string;
  accountSequence: string;  // 账户序列号，格式: D + YYMMDD + 5位序号，如 D2512110008
  username: string;
  threshold: number;
  totalParties: number;
  requireDelegate: boolean;
}

export interface SigningRequestedPayload {
  sessionId: string;
  userId: string;
  username: string;
  messageHash: string;
  userShare?: string;
  source?: string;  // 'identity-service' | 'blockchain-service'
}

export type MessageHandler = (topic: string, payload: Record<string, unknown>) => Promise<void>;

@Injectable()
export class EventConsumerService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(EventConsumerService.name);
  private kafka: Kafka;
  private consumer: Consumer;
  private isConnected = false;
  private handlers: Map<string, MessageHandler> = new Map();

  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') || 'mpc-service';
    const groupId = this.configService.get<string>('KAFKA_GROUP_ID') || 'mpc-service-group';

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

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

    this.consumer = this.kafka.consumer({
      groupId,
      sessionTimeout: 300000, // 5 minutes - MPC keygen can take a while
      heartbeatInterval: 10000, // 10 seconds
      maxWaitTimeInMs: 5000,
      rebalanceTimeout: 60000, // 1 minute - just for joining group, not for processing
    });

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

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

  /**
   * Subscribe to a topic with a handler
   */
  async subscribe(topic: string, handler: MessageHandler): Promise<void> {
    if (!this.isConnected) {
      this.logger.warn(`[SUBSCRIBE] Kafka not connected, cannot subscribe to ${topic}`);
      return;
    }

    this.handlers.set(topic, handler);
    this.logger.log(`[SUBSCRIBE] Registering handler for topic: ${topic}`);

    try {
      await this.consumer.subscribe({ topic, fromBeginning: false });
      this.logger.log(`[SUBSCRIBE] Successfully subscribed to topic: ${topic}`);
    } catch (error) {
      this.logger.error(`[ERROR] Failed to subscribe to topic: ${topic}`, error);
      throw error;
    }
  }

  /**
   * Start consuming messages
   */
  async startConsuming(): Promise<void> {
    if (!this.isConnected) {
      this.logger.warn(`[START] Kafka not connected, cannot start consuming`);
      return;
    }

    this.logger.log(`[START] Starting message consumption...`);
    this.logger.log(`[START] Registered handlers for topics: ${Array.from(this.handlers.keys()).join(', ')}`);

    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}`);

        const handler = this.handlers.get(topic);
        if (!handler) {
          this.logger.warn(`[RECEIVE] No handler registered for topic: ${topic}`);
          return;
        }

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

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

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

          this.logger.log(`[RECEIVE] Parsed event: eventType=${parsed.eventType || 'unknown'}`);
          this.logger.log(`[RECEIVE] Payload keys: ${Object.keys(payload).join(', ')}`);

          this.logger.log(`[HANDLE] Invoking handler for ${topic}...`);
          await handler(topic, payload);
          this.logger.log(`[HANDLE] Handler completed for ${topic}`);
        } catch (error) {
          this.logger.error(`[ERROR] Error processing message from ${topic}`, error);
        }
      },
    });

    this.logger.log(`[START] Started consuming messages successfully`);
  }
}
