/**
 * MPC Client Service
 *
 * 与 mpc-service (NestJS) 通信的客户端服务
 *
 * 支持两种模式:
 * 1. 同步模式 (legacy): 直接 HTTP 调用 + 轮询等待结果
 * 2. 事件驱动模式 (推荐): 发布 Kafka 事件，异步接收结果
 *
 * 调用路径 (DDD 分领域):
 * identity-service (身份域) → mpc-service (MPC域/NestJS) → mpc-system (Go/TSS实现)
 *
 * 事件驱动流程：
 * 1. identity-service 发布 mpc.KeygenRequested 事件
 * 2. mpc-service 消费事件，协调 mpc-system 完成 TSS keygen
 * 3. mpc-service 发布 mpc.KeygenCompleted 事件
 * 4. identity-service 消费事件，更新用户钱包地址
 */

import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { firstValueFrom } from 'rxjs';
import { createHash, randomUUID } from 'crypto';
import {
  EventPublisherService,
  IDENTITY_TOPICS,
} from '../../kafka/event-publisher.service';
import {
  MpcEventConsumerService,
  KeygenCompletedPayload,
  SigningCompletedPayload,
  SessionFailedPayload,
} from '../../kafka/mpc-event-consumer.service';

// MPC Request Topics (发布到 mpc-service)
export const MPC_REQUEST_TOPICS = {
  KEYGEN_REQUESTED: 'mpc.KeygenRequested',
  SIGNING_REQUESTED: 'mpc.SigningRequested',
} as const;

export interface KeygenRequest {
  sessionId: string;
  username: string; // 用户名 (自动递增ID)
  threshold: number; // t in t-of-n (默认 1, 即 2-of-3)
  totalParties: number; // n in t-of-n (默认 3)
  requireDelegate: boolean; // 是否需要 delegate party
}

export interface KeygenResult {
  sessionId: string;
  publicKey: string; // 压缩格式公钥 (33 bytes hex)
  delegateShare: DelegateShare; // delegate share (用户分片)
  serverParties: string[]; // 服务器 party IDs
}

export interface DelegateShare {
  partyId: string;
  partyIndex: number;
  encryptedShare: string; // 加密的分片数据 (hex)
}

export interface SigningRequest {
  username: string;
  messageHash: string; // 32 bytes hex
  userShare?: string; // 如果账户有 delegate share，需要传入用户分片
}

export interface SigningResult {
  sessionId: string;
  signature: string; // 64 bytes hex (R + S)
  messageHash: string;
}

// 异步请求接口 (事件驱动模式)
export interface AsyncKeygenRequest {
  userId: string;
  username: string;
  threshold: number;
  totalParties: number;
  requireDelegate: boolean;
}

export interface AsyncKeygenResponse {
  sessionId: string;
  status: 'pending' | 'processing';
}

export interface AsyncSigningRequest {
  userId: string;
  username: string;
  messageHash: string;
  userShare?: string;
}

export interface AsyncSigningResponse {
  sessionId: string;
  status: 'pending' | 'processing';
}

// 结果回调类型
export type KeygenResultCallback = (
  result: KeygenResult | null,
  error?: string,
) => Promise<void>;
export type SigningResultCallback = (
  result: SigningResult | null,
  error?: string,
) => Promise<void>;

@Injectable()
export class MpcClientService implements OnModuleInit {
  private readonly logger = new Logger(MpcClientService.name);
  private readonly mpcServiceUrl: string; // mpc-service (NestJS) URL
  private readonly mpcMode: string;
  private readonly useEventDriven: boolean;
  private readonly pollIntervalMs = 2000;
  private readonly maxPollAttempts = 150; // 5 minutes max

  // 待处理的 keygen/signing 请求回调
  private pendingKeygenCallbacks: Map<string, KeygenResultCallback> = new Map();
  private pendingSigningCallbacks: Map<string, SigningResultCallback> =
    new Map();

  constructor(
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
    private readonly eventPublisher: EventPublisherService,
    private readonly mpcEventConsumer: MpcEventConsumerService,
  ) {
    // 连接 mpc-service (NestJS)
    this.mpcServiceUrl = this.configService.get<string>(
      'MPC_SERVICE_URL',
      'http://localhost:3001',
    );
    this.mpcMode = this.configService.get<string>('MPC_MODE', 'local');
    this.useEventDriven =
      this.configService.get<string>('MPC_USE_EVENT_DRIVEN', 'true') === 'true';
  }

  async onModuleInit() {
    // 注册 MPC 事件处理器
    this.mpcEventConsumer.onKeygenCompleted(
      this.handleKeygenCompleted.bind(this),
    );
    this.mpcEventConsumer.onSigningCompleted(
      this.handleSigningCompleted.bind(this),
    );
    this.mpcEventConsumer.onSessionFailed(this.handleSessionFailed.bind(this));
    this.logger.log('MPC event handlers registered');
  }

  // ==========================================================================
  // 事件驱动模式 - 异步 API
  // ==========================================================================

  /**
   * 异步发起 keygen 请求 (事件驱动)
   * 立即返回 sessionId，结果通过回调处理
   */
  async requestKeygenAsync(
    request: AsyncKeygenRequest,
    callback?: KeygenResultCallback,
  ): Promise<AsyncKeygenResponse> {
    const sessionId = this.generateSessionId();

    this.logger.log(
      `Requesting async keygen: userId=${request.userId}, sessionId=${sessionId}`,
    );

    // 如果是本地模式，直接执行并回调
    if (this.mpcMode === 'local') {
      this.executeLocalKeygenWithCallback(sessionId, request, callback);
      return { sessionId, status: 'processing' };
    }

    // 注册回调
    if (callback) {
      this.pendingKeygenCallbacks.set(sessionId, callback);
    }

    // 发布 keygen 请求事件
    await this.eventPublisher.publish(MPC_REQUEST_TOPICS.KEYGEN_REQUESTED, {
      eventId: sessionId,
      eventType: 'KeygenRequested',
      occurredAt: new Date().toISOString(),
      aggregateId: request.userId,
      aggregateType: 'UserAccount',
      payload: {
        sessionId,
        userId: request.userId,
        username: request.username,
        threshold: request.threshold,
        totalParties: request.totalParties,
        requireDelegate: request.requireDelegate,
      },
    });

    this.logger.log(`Keygen request published: sessionId=${sessionId}`);
    return { sessionId, status: 'pending' };
  }

  /**
   * 异步发起 signing 请求 (事件驱动)
   */
  async requestSigningAsync(
    request: AsyncSigningRequest,
    callback?: SigningResultCallback,
  ): Promise<AsyncSigningResponse> {
    const sessionId = this.generateSessionId();

    this.logger.log(
      `Requesting async signing: userId=${request.userId}, sessionId=${sessionId}`,
    );

    // 如果是本地模式，直接执行并回调
    if (this.mpcMode === 'local') {
      this.executeLocalSigningWithCallback(sessionId, request, callback);
      return { sessionId, status: 'processing' };
    }

    // 注册回调
    if (callback) {
      this.pendingSigningCallbacks.set(sessionId, callback);
    }

    // 发布 signing 请求事件
    await this.eventPublisher.publish(MPC_REQUEST_TOPICS.SIGNING_REQUESTED, {
      eventId: sessionId,
      eventType: 'SigningRequested',
      occurredAt: new Date().toISOString(),
      aggregateId: request.userId,
      aggregateType: 'UserAccount',
      payload: {
        sessionId,
        userId: request.userId,
        username: request.username,
        messageHash: request.messageHash,
        userShare: request.userShare,
      },
    });

    this.logger.log(`Signing request published: sessionId=${sessionId}`);
    return { sessionId, status: 'pending' };
  }

  // ==========================================================================
  // 事件处理器 - 处理 MPC 完成事件
  // ==========================================================================

  private async handleKeygenCompleted(
    payload: KeygenCompletedPayload,
  ): Promise<void> {
    const sessionId = payload.sessionId;
    const callback = this.pendingKeygenCallbacks.get(sessionId);

    this.logger.log(`Keygen completed event received: sessionId=${sessionId}`);

    if (callback) {
      try {
        const result: KeygenResult = {
          sessionId,
          publicKey: payload.publicKey,
          delegateShare: payload.extraPayload?.delegateShare || {
            partyId: payload.partyId,
            partyIndex: 0,
            encryptedShare: '',
          },
          serverParties: payload.extraPayload?.serverParties || [],
        };
        await callback(result);
      } catch (error) {
        this.logger.error(
          `Keygen callback error: sessionId=${sessionId}`,
          error,
        );
      } finally {
        this.pendingKeygenCallbacks.delete(sessionId);
      }
    }
  }

  private async handleSigningCompleted(
    payload: SigningCompletedPayload,
  ): Promise<void> {
    const sessionId = payload.sessionId;
    const callback = this.pendingSigningCallbacks.get(sessionId);

    this.logger.log(`Signing completed event received: sessionId=${sessionId}`);

    if (callback) {
      try {
        const result: SigningResult = {
          sessionId,
          signature: payload.signature,
          messageHash: payload.messageHash,
        };
        await callback(result);
      } catch (error) {
        this.logger.error(
          `Signing callback error: sessionId=${sessionId}`,
          error,
        );
      } finally {
        this.pendingSigningCallbacks.delete(sessionId);
      }
    }
  }

  private async handleSessionFailed(
    payload: SessionFailedPayload,
  ): Promise<void> {
    const sessionId = payload.sessionId;
    const sessionType = payload.sessionType;

    this.logger.warn(
      `Session failed event received: sessionId=${sessionId}, type=${sessionType}`,
    );

    if (sessionType === 'keygen') {
      const callback = this.pendingKeygenCallbacks.get(sessionId);
      if (callback) {
        await callback(null, payload.errorMessage);
        this.pendingKeygenCallbacks.delete(sessionId);
      }
    } else if (sessionType === 'sign') {
      const callback = this.pendingSigningCallbacks.get(sessionId);
      if (callback) {
        await callback(null, payload.errorMessage);
        this.pendingSigningCallbacks.delete(sessionId);
      }
    }
  }

  // ==========================================================================
  // 本地模式辅助方法
  // ==========================================================================

  private async executeLocalKeygenWithCallback(
    sessionId: string,
    request: AsyncKeygenRequest,
    callback?: KeygenResultCallback,
  ): Promise<void> {
    try {
      const result = await this.executeLocalKeygen({
        sessionId,
        username: request.username,
        threshold: request.threshold,
        totalParties: request.totalParties,
        requireDelegate: request.requireDelegate,
      });
      if (callback) {
        await callback(result);
      }
    } catch (error) {
      if (callback) {
        await callback(
          null,
          error instanceof Error ? error.message : 'Unknown error',
        );
      }
    }
  }

  private async executeLocalSigningWithCallback(
    sessionId: string,
    request: AsyncSigningRequest,
    callback?: SigningResultCallback,
  ): Promise<void> {
    try {
      const result = await this.executeLocalSigning({
        username: request.username,
        messageHash: request.messageHash,
        userShare: request.userShare,
      });
      if (callback) {
        await callback(result);
      }
    } catch (error) {
      if (callback) {
        await callback(
          null,
          error instanceof Error ? error.message : 'Unknown error',
        );
      }
    }
  }

  // ==========================================================================
  // 同步模式 (Legacy) - 保留兼容性
  // ==========================================================================

  /**
   * 生成新的会话ID (必须是纯 UUID 格式)
   */
  generateSessionId(): string {
    return randomUUID();
  }

  /**
   * 执行 2-of-3 MPC 密钥生成 (带 delegate party)
   *
   * 三个参与方:
   * - Party 0 (SERVER): server-party-1，服务端持有
   * - Party 1 (SERVER): server-party-2，服务端持有
   * - Party 2 (DELEGATE): delegate-party，代理生成后返回给用户设备
   *
   * 调用路径: identity-service → mpc-service → mpc-system
   */
  async executeKeygen(request: KeygenRequest): Promise<KeygenResult> {
    this.logger.log(
      `Starting MPC keygen: username=${request.username}, t=${request.threshold}, n=${request.totalParties}`,
    );

    // 开发模式使用本地模拟
    if (this.mpcMode === 'local') {
      return this.executeLocalKeygen(request);
    }

    try {
      // Step 1: 调用 mpc-service 创建 keygen session
      const createResponse = await firstValueFrom(
        this.httpService.post<{
          sessionId: string;
          status: string;
        }>(
          `${this.mpcServiceUrl}/api/v1/mpc/keygen`,
          {
            username: request.username,
            thresholdN: request.totalParties,
            thresholdT: request.threshold,
            requireDelegate: request.requireDelegate,
          },
          {
            headers: {
              'Content-Type': 'application/json',
            },
            timeout: 30000,
          },
        ),
      );

      const sessionId = createResponse.data.sessionId;
      this.logger.log(`Keygen session created: ${sessionId}`);

      // Step 2: 轮询 session 状态直到完成
      const sessionResult = await this.pollKeygenStatus(sessionId);

      if (sessionResult.status !== 'completed') {
        throw new Error(
          `Keygen session failed with status: ${sessionResult.status}`,
        );
      }

      this.logger.log(`Keygen completed: publicKey=${sessionResult.publicKey}`);

      return {
        sessionId,
        publicKey: sessionResult.publicKey,
        delegateShare: sessionResult.delegateShare,
        serverParties: sessionResult.serverParties,
      };
    } catch (error) {
      this.logger.error(
        `MPC keygen failed: username=${request.username}`,
        error,
      );
      throw new Error(`MPC keygen failed: ${error.message}`);
    }
  }

  /**
   * 轮询 keygen session 状态
   */
  private async pollKeygenStatus(sessionId: string): Promise<{
    status: string;
    publicKey: string;
    delegateShare: DelegateShare;
    serverParties: string[];
  }> {
    for (let i = 0; i < this.maxPollAttempts; i++) {
      try {
        const response = await firstValueFrom(
          this.httpService.get<{
            sessionId: string;
            status: string;
            publicKey?: string;
            delegateShare?: {
              partyId: string;
              partyIndex: number;
              encryptedShare: string;
            };
            serverParties?: string[];
          }>(`${this.mpcServiceUrl}/api/v1/mpc/keygen/${sessionId}/status`, {
            timeout: 10000,
          }),
        );

        const data = response.data;
        this.logger.debug(`Session ${sessionId} status: ${data.status}`);

        if (data.status === 'completed') {
          return {
            status: 'completed',
            publicKey: data.publicKey || '',
            delegateShare: data.delegateShare || {
              partyId: '',
              partyIndex: -1,
              encryptedShare: '',
            },
            serverParties: data.serverParties || [],
          };
        }

        if (data.status === 'failed' || data.status === 'expired') {
          return {
            status: data.status,
            publicKey: '',
            delegateShare: { partyId: '', partyIndex: -1, encryptedShare: '' },
            serverParties: [],
          };
        }

        await this.sleep(this.pollIntervalMs);
      } catch (error) {
        this.logger.warn(`Error polling session status: ${error.message}`);
        await this.sleep(this.pollIntervalMs);
      }
    }

    throw new Error(
      `Session ${sessionId} timed out after ${this.maxPollAttempts * this.pollIntervalMs}ms`,
    );
  }

  /**
   * 执行 MPC 签名
   *
   * 调用路径: identity-service → mpc-service → mpc-system
   */
  async executeSigning(request: SigningRequest): Promise<SigningResult> {
    this.logger.log(
      `Starting MPC signing: username=${request.username}, messageHash=${request.messageHash}`,
    );

    // 开发模式使用本地模拟
    if (this.mpcMode === 'local') {
      return this.executeLocalSigning(request);
    }

    try {
      // 调用 mpc-service 创建签名 session
      const createResponse = await firstValueFrom(
        this.httpService.post<{
          sessionId: string;
          status: string;
        }>(
          `${this.mpcServiceUrl}/api/v1/mpc/sign`,
          {
            username: request.username,
            messageHash: request.messageHash,
            userShare: request.userShare,
          },
          {
            headers: {
              'Content-Type': 'application/json',
            },
            timeout: 30000,
          },
        ),
      );

      const sessionId = createResponse.data.sessionId;
      this.logger.log(`Signing session created: ${sessionId}`);

      // 轮询签名状态
      const signResult = await this.pollSigningStatus(sessionId);

      if (signResult.status !== 'completed') {
        throw new Error(
          `Signing session failed with status: ${signResult.status}`,
        );
      }

      return {
        sessionId,
        signature: signResult.signature,
        messageHash: request.messageHash,
      };
    } catch (error) {
      this.logger.error(
        `MPC signing failed: username=${request.username}`,
        error,
      );
      throw new Error(`MPC signing failed: ${error.message}`);
    }
  }

  /**
   * 轮询签名 session 状态
   */
  private async pollSigningStatus(sessionId: string): Promise<{
    status: string;
    signature: string;
  }> {
    for (let i = 0; i < this.maxPollAttempts; i++) {
      try {
        const response = await firstValueFrom(
          this.httpService.get<{
            sessionId: string;
            status: string;
            signature?: string;
          }>(`${this.mpcServiceUrl}/api/v1/mpc/sign/${sessionId}/status`, {
            timeout: 10000,
          }),
        );

        const data = response.data;

        if (data.status === 'completed') {
          return {
            status: 'completed',
            signature: data.signature || '',
          };
        }

        if (data.status === 'failed' || data.status === 'expired') {
          return {
            status: data.status,
            signature: '',
          };
        }

        await this.sleep(this.pollIntervalMs);
      } catch (error) {
        this.logger.warn(`Error polling signing status: ${error.message}`);
        await this.sleep(this.pollIntervalMs);
      }
    }

    throw new Error(`Signing session ${sessionId} timed out`);
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  /**
   * 执行本地模拟的 MPC keygen (用于开发测试)
   */
  async executeLocalKeygen(request: KeygenRequest): Promise<KeygenResult> {
    this.logger.log(
      `Starting LOCAL MPC keygen (test mode): username=${request.username}`,
    );

    const { ethers } = await import('ethers');

    // 生成随机私钥
    const wallet = ethers.Wallet.createRandom();
    const publicKey = wallet.publicKey;

    // 压缩公钥 (33 bytes)
    const compressedPubKey = ethers.SigningKey.computePublicKey(
      publicKey,
      true,
    );

    // 模拟 delegate share
    const delegateShare: DelegateShare = {
      partyId: 'delegate-party',
      partyIndex: 2,
      encryptedShare: this.encryptShareData(
        wallet.privateKey,
        request.username,
      ),
    };

    return {
      sessionId: this.generateSessionId(),
      publicKey: compressedPubKey.slice(2), // 去掉 0x 前缀
      delegateShare,
      serverParties: ['server-party-1', 'server-party-2'],
    };
  }

  /**
   * 执行本地模拟的 MPC 签名 (用于开发测试)
   */
  async executeLocalSigning(request: SigningRequest): Promise<SigningResult> {
    this.logger.log(
      `Starting LOCAL MPC signing (test mode): username=${request.username}`,
    );

    const { ethers } = await import('ethers');

    const messageHashBytes = Buffer.from(request.messageHash, 'hex');

    // 创建一个临时钱包用于签名 (仅测试)
    const testWallet = new ethers.Wallet(
      '0x' + createHash('sha256').update(request.username).digest('hex'),
    );

    const signature = testWallet.signingKey.sign(messageHashBytes);

    // 返回 R + S 格式 (64 bytes hex)
    return {
      sessionId: this.generateSessionId(),
      signature: signature.r.slice(2) + signature.s.slice(2),
      messageHash: request.messageHash,
    };
  }

  /**
   * 加密分片数据 (简化版本)
   */
  private encryptShareData(data: string, key: string): string {
    const cipher = createHash('sha256').update(key).digest('hex');
    return Buffer.from(data).toString('base64') + '.' + cipher.slice(0, 16);
  }

  /**
   * 计算消息摘要 (用于签名)
   */
  computeMessageHash(message: string): string {
    return createHash('sha256').update(message).digest('hex');
  }
}
