// GPN Message Handler
// SME envelope wrapping for PACS.008, PACS.002, FXMT.003, CBDC_TX, SCB_NOTIFY, DBIS_COMMIT

import { v4 as uuidv4 } from 'uuid';
import { logger } from '@/infrastructure/monitoring/logger';
import { iso20022Service } from '@/integration/iso20022/iso20022.service';
import { IsoMessageType, Pacs008Message } from '@/shared/types';
import prisma from '@/shared/database/prisma';

export interface SovereignMessageEnvelope {
  envelopeId: string;
  messageType: string;
  sovereignBankId: string;
  timestamp: Date;
  messageHash: string;
  encryptedPayload: string;
  signature: string;
}

export interface GpnMessageRequest {
  messageType: string;
  sovereignBankId: string;
  messageData: Record<string, unknown>;
}

export class GpnMessageHandlerService {
  /**
   * Wrap message in Sovereign Message Envelope (SME)
   */
  async wrapInSmeEnvelope(request: GpnMessageRequest): Promise<SovereignMessageEnvelope> {
    const envelopeId = `SME-${uuidv4()}`;
    const timestamp = new Date();

    // Serialize message data
    const messageDataString = JSON.stringify(request.messageData);
    const messageHash = this.hashMessage(messageDataString);

    // Encrypt payload (in production, use PQ-encryption)
    const encryptedPayload = this.encryptPayload(messageDataString);

    // Sign envelope (in production, use HSM-backed signature)
    const signature = await this.signEnvelope(envelopeId, request.sovereignBankId, messageHash);

    const envelope: SovereignMessageEnvelope = {
      envelopeId,
      messageType: request.messageType,
      sovereignBankId: request.sovereignBankId,
      timestamp,
      messageHash,
      encryptedPayload,
      signature,
    };

    logger.info(`GPN: Wrapped message in SME envelope`, {
      envelopeId,
      messageType: request.messageType,
      sovereignBankId: request.sovereignBankId,
    });

    return envelope;
  }

  /**
   * Process PACS.008 message
   */
  async processPacs008(
    message: Pacs008Message,
    sovereignBankId: string
  ): Promise<{ paymentId: string; envelopeId: string }> {
    // Generate PACS.008 XML
    const xmlMessage = await iso20022Service.generatePacs008(message);

    // Wrap in SME envelope
    const envelope = await this.wrapInSmeEnvelope({
      messageType: 'PACS.008',
      sovereignBankId,
      messageData: {
        xml: xmlMessage,
        message: message,
      },
    });

    // Create GPN payment record
    const paymentId = `GPN-PAY-${uuidv4()}`;
    await prisma.gpn_payments.create({
      data: {
        paymentId,
        sourceBankId: sovereignBankId,
        destinationBankId: message.creditTransferTransactionInfo.creditor.financialInstitution.bicfi,
        amount: parseFloat(message.creditTransferTransactionInfo.interbankSettlementAmount.amount),
        currencyCode: message.creditTransferTransactionInfo.interbankSettlementAmount.currency,
        assetType: 'fiat',
        paymentType: 'bank_to_bank',
        layer1Status: 'authenticated',
        layer2Status: 'pending',
        layer3Status: 'pending',
        status: 'processing',
        isoMessageId: message.messageId,
        smeEnvelope: envelope as unknown as Record<string, unknown>,
      },
    });

    return { paymentId, envelopeId: envelope.envelopeId };
  }

  /**
   * Process PACS.002 (Payment Status Report)
   */
  async processPacs002(
    originalMessageId: string,
    status: string,
    sovereignBankId: string
  ): Promise<string> {
    const envelope = await this.wrapInSmeEnvelope({
      messageType: 'PACS.002',
      sovereignBankId,
      messageData: {
        originalMessageId,
        status,
        timestamp: new Date().toISOString(),
      },
    });

    return envelope.envelopeId;
  }

  /**
   * Process FXMT.003 (FX Trade Execution Report)
   */
  async processFxmt003(
    tradeData: Record<string, unknown>,
    sovereignBankId: string
  ): Promise<string> {
    const envelope = await this.wrapInSmeEnvelope({
      messageType: 'FXMT.003',
      sovereignBankId,
      messageData: tradeData,
    });

    return envelope.envelopeId;
  }

  /**
   * Process CBDC_TX message
   */
  async processCbdcTx(
    transactionData: Record<string, unknown>,
    sovereignBankId: string
  ): Promise<string> {
    const envelope = await this.wrapInSmeEnvelope({
      messageType: 'CBDC_TX',
      sovereignBankId,
      messageData: transactionData,
    });

    return envelope.envelopeId;
  }

  /**
   * Process SCB_NOTIFY message
   */
  async processScbNotify(
    notificationData: Record<string, unknown>,
    sovereignBankId: string
  ): Promise<string> {
    const envelope = await this.wrapInSmeEnvelope({
      messageType: 'SCB_NOTIFY',
      sovereignBankId,
      messageData: notificationData,
    });

    return envelope.envelopeId;
  }

  /**
   * Process DBIS_COMMIT message
   */
  async processDbisCommit(
    commitData: Record<string, unknown>,
    sovereignBankId: string
  ): Promise<string> {
    const envelope = await this.wrapInSmeEnvelope({
      messageType: 'DBIS_COMMIT',
      sovereignBankId,
      messageData: commitData,
    });

    return envelope.envelopeId;
  }

  /**
   * Hash message data
   */
  private hashMessage(data: string): string {
    const crypto = require('crypto');
    return crypto.createHash('sha256').update(data).digest('hex');
  }

  /**
   * Encrypt payload (simplified - in production use PQ-encryption)
   */
  private encryptPayload(data: string): string {
    // In production, use Kyber or other PQ-encryption
    // For now, return base64 encoded (not secure, just for structure)
    return Buffer.from(data).toString('base64');
  }

  /**
   * Sign envelope (simplified - in production use HSM)
   */
  private async signEnvelope(
    envelopeId: string,
    sovereignBankId: string,
    messageHash: string
  ): Promise<string> {
    // In production, use HSM-backed signature
    // For now, return a placeholder signature
    const signatureData = `${envelopeId}:${sovereignBankId}:${messageHash}`;
    return this.hashMessage(signatureData);
  }
}

export const gpnMessageHandlerService = new GpnMessageHandlerService();

