// M-RTGS Settlement Service
// Multi-asset settlement < 100ms, multi-ledger synchronization

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '@/infrastructure/monitoring/logger';
import { ledgerService } from '@/core/ledger/ledger.service';
import { ledgerLifecycleService } from '@/core/ledger/ledger-lifecycle.service';
import { LedgerEntryType } from '@/shared/types';


export interface MrtgsSettlementRequest {
  queueId: string;
  paymentId: string;
}

export interface MrtgsSettlementResult {
  settlementId: string;
  settlementTime: number; // Milliseconds
  status: 'settled' | 'failed';
  ledgerSyncStatus: Record<string, boolean>;
}

export class MrtgsSettlementService {
  /**
   * Process M-RTGS settlement (< 100ms target)
   */
  async processSettlement(request: MrtgsSettlementRequest): Promise<MrtgsSettlementResult> {
    const startTime = Date.now();
    const settlementId = `MRTGS-SETTLE-${uuidv4()}`;

    try {
      // Get queue entry
      const queueEntry = await prisma.mrtgs_queues.findUnique({
        where: { queueId: request.queueId },
      });

      if (!queueEntry) {
        throw new Error('Queue entry not found');
      }

      // Get accounts
      const sourceAccount = await this.getAccount(
        queueEntry.sourceBankId,
        queueEntry.currencyCode,
        queueEntry.assetType
      );
      const destAccount = await this.getAccount(
        queueEntry.destinationBankId,
        queueEntry.currencyCode,
        queueEntry.assetType
      );

      if (!sourceAccount || !destAccount) {
        throw new Error('Accounts not found');
      }

      // Process multi-ledger synchronization
      const ledgerSyncStatus = await this.synchronizeMultiLedgers(
        sourceAccount.id,
        destAccount.id,
        queueEntry.amount.toString(),
        queueEntry.currencyCode,
        queueEntry.assetType,
        queueEntry.paymentId
      );

      // Calculate settlement time
      const settlementTime = Date.now() - startTime;

      // Create settlement record
      await prisma.mrtgs_settlements.create({
        data: {
          settlementId,
          queueId: request.queueId,
          paymentId: queueEntry.paymentId,
          assetType: queueEntry.assetType,
          amount: queueEntry.amount,
          currencyCode: queueEntry.currencyCode,
          sourceBankId: queueEntry.sourceBankId,
          destinationBankId: queueEntry.destinationBankId,
          settlementTime,
          ledgerSyncStatus: ledgerSyncStatus as unknown as Record<string, unknown>,
          status: 'settled',
          settledAt: new Date(),
        },
      });

      // Update queue status
      await prisma.mrtgs_queues.update({
        where: { queueId: request.queueId },
        data: { status: 'settled' },
      });

      logger.info(`M-RTGS: Settlement completed`, {
        settlementId,
        paymentId: queueEntry.paymentId,
        settlementTime,
        ledgerSyncStatus,
      });

      return {
        settlementId,
        settlementTime,
        status: 'settled',
        ledgerSyncStatus,
      };
    } catch (error) {
      const settlementTime = Date.now() - startTime;

      logger.error('M-RTGS: Settlement failed', {
        error,
        settlementId,
        settlementTime,
      });

      // Create failed settlement record
      await prisma.mrtgs_settlements.create({
        data: {
          settlementId,
          queueId: request.queueId,
          paymentId: request.paymentId,
          assetType: 'fiat',
          amount: new Decimal(0),
          currencyCode: 'USD',
          sourceBankId: '',
          destinationBankId: '',
          settlementTime,
          ledgerSyncStatus: {},
          status: 'failed',
        },
      });

      return {
        settlementId,
        settlementTime,
        status: 'failed',
        ledgerSyncStatus: {},
      };
    }
  }

  /**
   * Get account for settlement
   */
  private async getAccount(
    sovereignBankId: string,
    currencyCode: string,
    assetType: string
  ) {
    return await prisma.bank_accounts.findFirst({
      where: {
        sovereignBankId,
        currencyCode,
        assetType,
        status: 'active',
        accountType: {
          in: ['settlement', 'treasury', 'sovereign'],
        },
      },
    });
  }

  /**
   * Synchronize across multiple ledgers
   */
  private async synchronizeMultiLedgers(
    sourceAccountId: string,
    destAccountId: string,
    amount: string,
    currencyCode: string,
    assetType: string,
    referenceId: string
  ): Promise<Record<string, boolean>> {
    const syncStatus: Record<string, boolean> = {};

    try {
      // Get source and destination banks
      const sourceAccount = await prisma.bank_accounts.findUnique({
        where: { id: sourceAccountId },
        include: { sovereignBank: true },
      });
      const destAccount = await prisma.bank_accounts.findUnique({
        where: { id: destAccountId },
        include: { sovereignBank: true },
      });

      if (!sourceAccount || !destAccount) {
        throw new Error('Accounts not found');
      }

      // Post to source bank ledger
      try {
        const sourceLedgerId = sourceAccount.sovereignBankId;
        await ledgerLifecycleService.processTransaction(
          sourceLedgerId,
          {
            referenceId,
            entries: [
              {
                debitAccountId: sourceAccountId,
                creditAccountId: destAccountId,
                amount,
                currencyCode,
                assetType: assetType as any,
              },
            ],
          },
          LedgerEntryType.TYPE_A,
          undefined,
          { mrtgs: true }
        );
        syncStatus[`${sourceAccount.sovereignBank.sovereignCode}_ledger`] = true;
      } catch (error) {
        logger.error('Failed to post to source ledger', { error });
        syncStatus[`${sourceAccount.sovereignBank.sovereignCode}_ledger`] = false;
      }

      // Post to destination bank ledger
      try {
        const destLedgerId = destAccount.sovereignBankId;
        await ledgerLifecycleService.processTransaction(
          destLedgerId,
          {
            referenceId,
            entries: [
              {
                debitAccountId: sourceAccountId,
                creditAccountId: destAccountId,
                amount,
                currencyCode,
                assetType: assetType as any,
              },
            ],
          },
          LedgerEntryType.TYPE_A,
          undefined,
          { mrtgs: true }
        );
        syncStatus[`${destAccount.sovereignBank.sovereignCode}_ledger`] = true;
      } catch (error) {
        logger.error('Failed to post to destination ledger', { error });
        syncStatus[`${destAccount.sovereignBank.sovereignCode}_ledger`] = false;
      }

      // Post to DBIS Master Ledger if cross-sovereign
      if (sourceAccount.sovereignBankId !== destAccount.sovereignBankId) {
        try {
          await ledgerService.postDoubleEntry(
            'Master',
            sourceAccountId,
            destAccountId,
            amount,
            currencyCode,
            assetType as any,
            LedgerEntryType.TYPE_A,
            referenceId
          );
          syncStatus['dbis_master_ledger'] = true;
        } catch (error) {
          logger.error('Failed to post to DBIS Master Ledger', { error });
          syncStatus['dbis_master_ledger'] = false;
        }
      }
    } catch (error) {
      logger.error('Multi-ledger synchronization failed', { error });
    }

    return syncStatus;
  }
}

export const mrtgsSettlementService = new MrtgsSettlementService();

