/**
 * MPC Signing Client
 *
 * 通过 Kafka 事件与 mpc-service 通信进行 MPC 签名
 * 用于热钱包的 ERC20 转账签名
 *
 * 事件流:
 * blockchain-service → Kafka(mpc.SigningRequested) → mpc-service
 * mpc-service → Kafka(mpc.SigningCompleted) → blockchain-service
 */

import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { randomUUID } from 'crypto';
import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service';
import {
  MpcEventConsumerService,
  SigningCompletedPayload,
  SessionFailedPayload,
} from '@/infrastructure/kafka/mpc-event-consumer.service';

export interface CreateSigningInput {
  username: string;
  messageHash: string;
}

export interface SigningResult {
  sessionId: string;
  status: string;
  signature?: string;
}

// 签名结果回调
type SigningCallback = (signature: string | null, error?: string) => void;

// MPC 签名请求 Topic
export const MPC_SIGNING_TOPIC = 'mpc.SigningRequested';

@Injectable()
export class MpcSigningClient implements OnModuleInit {
  private readonly logger = new Logger(MpcSigningClient.name);
  private readonly hotWalletUsername: string;
  private readonly hotWalletAddress: string;
  private readonly signingTimeoutMs: number = 300000; // 5 minutes

  // 待处理的签名请求回调 Map<sessionId, { resolve, reject, timeout }>
  private pendingRequests: Map<string, {
    resolve: (signature: string) => void;
    reject: (error: Error) => void;
    timeout: NodeJS.Timeout;
  }> = new Map();

  constructor(
    private readonly configService: ConfigService,
    private readonly eventPublisher: EventPublisherService,
    private readonly mpcEventConsumer: MpcEventConsumerService,
  ) {
    this.hotWalletUsername = this.configService.get<string>('HOT_WALLET_USERNAME', '');
    this.hotWalletAddress = this.configService.get<string>('HOT_WALLET_ADDRESS', '');

    if (!this.hotWalletUsername) {
      this.logger.warn('[INIT] HOT_WALLET_USERNAME not configured');
    }
    if (!this.hotWalletAddress) {
      this.logger.warn('[INIT] HOT_WALLET_ADDRESS not configured');
    }

    this.logger.log(`[INIT] Hot Wallet Username: ${this.hotWalletUsername || '(not configured)'}`);
    this.logger.log(`[INIT] Hot Wallet Address: ${this.hotWalletAddress || '(not configured)'}`);
    this.logger.log(`[INIT] Using Kafka event-driven signing`);
  }

  async onModuleInit() {
    // 注册签名完成和失败事件处理器
    this.mpcEventConsumer.onSigningCompleted(this.handleSigningCompleted.bind(this));
    this.mpcEventConsumer.onSigningFailed(this.handleSigningFailed.bind(this));
    this.logger.log('[INIT] MPC signing event handlers registered');
  }

  /**
   * 检查热钱包是否已配置
   */
  isConfigured(): boolean {
    return !!this.hotWalletUsername && !!this.hotWalletAddress;
  }

  /**
   * 获取热钱包地址
   */
  getHotWalletAddress(): string {
    return this.hotWalletAddress;
  }

  /**
   * 获取热钱包用户名
   */
  getHotWalletUsername(): string {
    return this.hotWalletUsername;
  }

  /**
   * 签名消息（通过 Kafka 事件驱动）
   *
   * @param messageHash 要签名的消息哈希 (hex string with 0x prefix)
   * @returns 签名结果 (hex string)
   */
  async signMessage(messageHash: string): Promise<string> {
    this.logger.log(`[SIGN] Starting MPC signing for: ${messageHash.slice(0, 16)}...`);

    if (!this.hotWalletUsername) {
      throw new Error('Hot wallet username not configured');
    }

    const sessionId = randomUUID();
    this.logger.log(`[SIGN] Session ID: ${sessionId}`);

    // 创建 Promise 等待签名结果
    const signaturePromise = new Promise<string>((resolve, reject) => {
      // 设置超时
      const timeout = setTimeout(() => {
        this.pendingRequests.delete(sessionId);
        reject(new Error(`MPC signing timeout after ${this.signingTimeoutMs}ms`));
      }, this.signingTimeoutMs);

      // 保存到待处理队列
      this.pendingRequests.set(sessionId, { resolve, reject, timeout });
    });

    // 发布签名请求事件到 Kafka
    try {
      await this.eventPublisher.publish({
        eventType: 'blockchain.mpc.signing.requested',
        toPayload: () => ({
          sessionId,
          userId: 'system', // 系统热钱包
          username: this.hotWalletUsername,
          messageHash,
          source: 'blockchain-service',
        }),
        eventId: sessionId,
        occurredAt: new Date(),
      });

      this.logger.log(`[SIGN] Signing request published to Kafka: sessionId=${sessionId}`);
    } catch (error) {
      // 发布失败，清理待处理队列
      const pending = this.pendingRequests.get(sessionId);
      if (pending) {
        clearTimeout(pending.timeout);
        this.pendingRequests.delete(sessionId);
      }
      throw error;
    }

    // 等待签名结果
    const signature = await signaturePromise;
    this.logger.log(`[SIGN] Signature obtained: ${signature.slice(0, 20)}...`);
    return signature;
  }

  /**
   * 处理签名完成事件
   */
  private async handleSigningCompleted(payload: SigningCompletedPayload): Promise<void> {
    const sessionId = payload.sessionId;
    this.logger.log(`[EVENT] Signing completed: sessionId=${sessionId}`);

    const pending = this.pendingRequests.get(sessionId);
    if (pending) {
      clearTimeout(pending.timeout);
      this.pendingRequests.delete(sessionId);

      if (payload.signature) {
        pending.resolve(payload.signature);
      } else {
        pending.reject(new Error('Signing completed but no signature returned'));
      }
    } else {
      this.logger.warn(`[EVENT] No pending request for sessionId=${sessionId}`);
    }
  }

  /**
   * 处理签名失败事件
   */
  private async handleSigningFailed(payload: SessionFailedPayload): Promise<void> {
    const sessionId = payload.sessionId;
    this.logger.warn(`[EVENT] Signing failed: sessionId=${sessionId}, error=${payload.errorMessage}`);

    const pending = this.pendingRequests.get(sessionId);
    if (pending) {
      clearTimeout(pending.timeout);
      this.pendingRequests.delete(sessionId);
      pending.reject(new Error(`MPC signing failed: ${payload.errorMessage}`));
    }
  }
}
