/**
 * SigningRequested Event Handler
 *
 * Handles signing requests from identity-service and blockchain-service via Kafka.
 * Processes the signing and publishes completion/failure events.
 *
 * 事件流:
 * - identity-service → mpc.SigningRequested → mpc-service → mpc.SigningCompleted → identity-service
 * - blockchain-service → mpc.SigningRequested → mpc-service → mpc.SigningCompleted → blockchain-service
 */

import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { MPCCoordinatorService } from '../services/mpc-coordinator.service';
import { EventPublisherService } from '../../infrastructure/messaging/kafka/event-publisher.service';
import {
  EventConsumerService,
  MPC_CONSUME_TOPICS,
  SigningRequestedPayload,
} from '../../infrastructure/messaging/kafka/event-consumer.service';
import { SigningCompletedEvent } from '../../domain/events/signing-completed.event';
import { SessionFailedEvent } from '../../domain/events/session-failed.event';
import { SessionType } from '../../domain/enums';

@Injectable()
export class SigningRequestedHandler implements OnModuleInit {
  private readonly logger = new Logger(SigningRequestedHandler.name);

  constructor(
    private readonly eventConsumer: EventConsumerService,
    private readonly eventPublisher: EventPublisherService,
    private readonly mpcCoordinator: MPCCoordinatorService,
  ) {}

  async onModuleInit() {
    await this.eventConsumer.subscribe(
      MPC_CONSUME_TOPICS.SIGNING_REQUESTED,
      this.handleMessage.bind(this),
    );
    this.logger.log(`Subscribed to ${MPC_CONSUME_TOPICS.SIGNING_REQUESTED}`);
  }

  private async handleMessage(topic: string, payload: Record<string, unknown>): Promise<void> {
    const data = payload as unknown as SigningRequestedPayload;
    const { sessionId, userId, username, messageHash, userShare } = data;
    // source 标识请求来源: 'blockchain-service' | 'identity-service'
    const source = (data as any).source || 'identity-service';

    this.logger.log(`Processing signing request: userId=${userId}, username=${username}, sessionId=${sessionId}, source=${source}`);

    // 规范化 messageHash: mpc-system 期望纯 hex 字符串（不带 0x 前缀）
    const normalizedMessageHash = messageHash?.startsWith('0x')
      ? messageHash.slice(2)
      : messageHash;

    this.logger.log(`Normalized messageHash: ${normalizedMessageHash?.slice(0, 16)}...`);

    try {
      // Step 1: Create signing session via mpc-system
      const createResult = await this.mpcCoordinator.createSigningSession({
        username,
        messageHash: normalizedMessageHash,
        userShare,
      });

      const mpcSessionId = createResult.sessionId;
      this.logger.log(`Signing session created in mpc-system: ${mpcSessionId}`);

      // Step 2: Poll for completion (with max retries)
      const result = await this.pollSigningCompletion(mpcSessionId, 150, 2000);

      if (result.status === 'completed' && result.signature) {
        // Publish success event
        const completedEvent = new SigningCompletedEvent(
          sessionId, // Original session ID from identity-service
          '', // partyId
          messageHash,
          result.signature,
          '', // publicKey - not needed for signing result
        );

        // Add extra payload for the requesting service (identity-service or blockchain-service)
        (completedEvent as any).extraPayload = {
          userId,
          username,
          mpcSessionId,
          source,
        };

        await this.eventPublisher.publishWithRetry(completedEvent);
        this.logger.log(`Signing completed: userId=${userId}, signature=${result.signature.substring(0, 16)}...`);
      } else {
        // Publish failure event
        const failedEvent = new SessionFailedEvent(
          sessionId,
          '', // partyId
          SessionType.SIGN,
          `Signing failed with status: ${result.status}`,
        );
        (failedEvent as any).extraPayload = { userId, username, source };

        await this.eventPublisher.publishWithRetry(failedEvent);
        this.logger.warn(`Signing failed: userId=${userId}, status=${result.status}, source=${source}`);
      }
    } catch (error) {
      this.logger.error(`Signing processing error: userId=${userId}`, error);

      // Publish failure event
      const failedEvent = new SessionFailedEvent(
        sessionId,
        '', // partyId
        SessionType.SIGN,
        error instanceof Error ? error.message : 'Unknown error',
      );
      (failedEvent as any).extraPayload = { userId, username, source };

      try {
        await this.eventPublisher.publishWithRetry(failedEvent);
      } catch (publishError) {
        this.logger.error('Failed to publish failure event', publishError);
      }
    }
  }

  private async pollSigningCompletion(
    sessionId: string,
    maxAttempts: number,
    intervalMs: number,
  ): Promise<{
    status: string;
    signature?: string;
  }> {
    for (let i = 0; i < maxAttempts; i++) {
      const status = await this.mpcCoordinator.getSigningStatus(sessionId);

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

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

      await this.sleep(intervalMs);
    }

    return { status: 'timeout' };
  }

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