// CIM Interledger Service
// CBDC cross-border conversion
// Dual-posting (SCB + DBIS) synchronization
// FX-linked CBDC conversions
// Interledger routing

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { ledgerPostingModule } from '@/core/ledger/ledger-posting.module';
import { createHash } from 'crypto';
import { AssetType, LedgerEntryType } from '@/shared/types';


export interface CimInterledgerConversionRequest {
  sourceSovereignBankId: string;
  targetSovereignBankId: string;
  sourceCbdcCode: string;
  targetCbdcCode: string;
  amount: string;
  conversionType: string;
  fxRate?: string;
}

export interface InterledgerConversionResult {
  conversionId: string;
  scbLedgerHash: string;
  dbisLedgerHash: string;
  status: string;
}

export class CimInterledgerService {
  /**
   * Execute CBDC interledger conversion
   */
  async convertCbdc(
    request: CimInterledgerConversionRequest
  ): Promise<InterledgerConversionResult> {
    const conversionId = `CIM-CONV-${uuidv4()}`;

    try {
      // Step 1: Calculate FX rate if not provided
      let fxRate = request.fxRate
        ? new Decimal(request.fxRate)
        : await this.calculateFxRate(
            request.sourceCbdcCode,
            request.targetCbdcCode
          );

      // Step 2: Post to SCB ledger (source)
      const scbHash = await this.postToScbLedger(request, conversionId);

      // Step 3: Post to DBIS master ledger
      const dbisHash = await this.postToDbisLedger(request, conversionId, fxRate);

      // Step 4: Create conversion record
      const conversion = await prisma.cim_interledger_conversions.create({
        data: {
          id: uuidv4(),
          conversionId,
          sourceSovereignBankId: request.sourceSovereignBankId,
          targetSovereignBankId: request.targetSovereignBankId,
          sourceCbdcCode: request.sourceCbdcCode,
          targetCbdcCode: request.targetCbdcCode,
          amount: new Decimal(request.amount),
          fxRate,
          conversionType: request.conversionType,
          dualPostingStatus: 'both_posted',
          scbLedgerHash: scbHash,
          dbisLedgerHash: dbisHash,
          status: 'completed',
          completedAt: new Date(),
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      });

      return {
        conversionId: conversion.conversionId,
        scbLedgerHash: conversion.scbLedgerHash || '',
        dbisLedgerHash: conversion.dbisLedgerHash || '',
        status: conversion.status,
      };
    } catch (error) {
      // Create failed conversion
      await prisma.cim_interledger_conversions.create({
        data: {
          id: uuidv4(),
          conversionId,
          sourceSovereignBankId: request.sourceSovereignBankId,
          targetSovereignBankId: request.targetSovereignBankId,
          sourceCbdcCode: request.sourceCbdcCode,
          targetCbdcCode: request.targetCbdcCode,
          amount: new Decimal(request.amount),
          fxRate: request.fxRate ? new Decimal(request.fxRate) : null,
          conversionType: request.conversionType,
          dualPostingStatus: 'pending',
          status: 'failed',
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      });

      throw error;
    }
  }

  /**
   * Calculate FX rate between two CBDCs
   */
  private async calculateFxRate(
    sourceCbdcCode: string,
    targetCbdcCode: string
  ): Promise<Decimal> {
    // In production, this would query the FX service
    // For now, return a default rate
    const fxPair = await prisma.fx_pairs.findFirst({
      where: {
        baseCurrency: sourceCbdcCode,
        quoteCurrency: targetCbdcCode,
        status: 'active',
      },
    });

    if (fxPair) {
      // Get latest trade price
      const latestTrade = await prisma.fx_trades.findFirst({
        where: {
          fxPairId: fxPair.id,
          status: 'executed',
        },
        orderBy: { executedAt: 'desc' },
      });

      if (latestTrade) {
        return new Decimal(latestTrade.price);
      }
    }

    // Default rate of 1:1 if no FX data available
    return new Decimal(1);
  }

  /**
   * Post to SCB ledger (source sovereign bank)
   */
  private async postToScbLedger(
    request: CimInterledgerConversionRequest,
    conversionId: string
  ): Promise<string> {
    // Get CBDC wallet for source
    const sourceWallet = await prisma.cbdc_wallets.findFirst({
      where: {
        sovereignBankId: request.sourceSovereignBankId,
        currencyCode: request.sourceCbdcCode,
        status: 'active',
      },
    });

    if (!sourceWallet) {
      throw new Error('Source CBDC wallet not found');
    }

    // Post debit entry to source wallet
    // In a real implementation, this would use the ledger service
    // For now, we'll generate a hash
    const entryData = {
      conversionId,
      sourceWalletId: sourceWallet.walletId,
      amount: request.amount,
      currencyCode: request.sourceCbdcCode,
      timestamp: new Date().toISOString(),
    };

    return createHash('sha3-256')
      .update(JSON.stringify(entryData))
      .digest('hex');
  }

  /**
   * Post to DBIS master ledger
   */
  private async postToDbisLedger(
    request: CimInterledgerConversionRequest,
    conversionId: string,
    fxRate: Decimal
  ): Promise<string> {
    // Get accounts
    const sourceAccounts = await prisma.bank_accounts.findMany({
      where: {
        sovereignBankId: request.sourceSovereignBankId,
        currencyCode: request.sourceCbdcCode,
        assetType: 'cbdc',
        status: 'active',
      },
      take: 1,
    });

    const targetAccounts = await prisma.bank_accounts.findMany({
      where: {
        sovereignBankId: request.targetSovereignBankId,
        currencyCode: request.targetCbdcCode,
        assetType: 'cbdc',
        status: 'active',
      },
      take: 1,
    });

    if (sourceAccounts.length === 0 || targetAccounts.length === 0) {
      throw new Error('Accounts not found for DBIS ledger posting');
    }

    // Calculate target amount
    const sourceAmount = new Decimal(request.amount);
    const targetAmount = sourceAmount.mul(fxRate);

    // Post to DBIS master ledger using atomic posting module
    const result = await ledgerPostingModule.postEntry({
      ledgerId: 'Master',
      debitAccountId: sourceAccounts[0].id,
      creditAccountId: targetAccounts[0].id,
      amount: targetAmount.toString(),
      currencyCode: request.targetCbdcCode,
      assetType: AssetType.CBDC,
      transactionType: LedgerEntryType.TYPE_A,
      referenceId: conversionId,
      metadata: {
        conversionType: request.conversionType,
        fxRate: fxRate.toString(),
        sourceAmount: request.amount,
      },
    });

    return result.blockHash;
  }

  /**
   * Synchronize dual-posting status
   */
  async synchronizeDualPosting(conversionId: string): Promise<void> {
    const conversion = await prisma.cim_interledger_conversions.findUnique({
      where: { conversionId },
    });

    if (!conversion) {
      throw new Error('Conversion not found');
    }

    // Check posting status
    let dualPostingStatus = conversion.dualPostingStatus;

    if (conversion.scbLedgerHash && !conversion.dbisLedgerHash) {
      dualPostingStatus = 'scb_posted';
    } else if (!conversion.scbLedgerHash && conversion.dbisLedgerHash) {
      dualPostingStatus = 'dbis_posted';
    } else if (conversion.scbLedgerHash && conversion.dbisLedgerHash) {
      dualPostingStatus = 'both_posted';
    }

    // Update status
    if (dualPostingStatus !== conversion.dualPostingStatus) {
      await prisma.cim_interledger_conversions.update({
        where: { conversionId },
        data: {
          dualPostingStatus,
          ...(dualPostingStatus === 'both_posted' && {
            status: 'completed',
            completedAt: new Date(),
          }),
        },
      });
    }
  }

  /**
   * Get interledger conversions
   */
  async getInterledgerConversions(
    sovereignBankId?: string,
    status?: string,
    limit: number = 100
  ) {
    return await prisma.cim_interledger_conversions.findMany({
      where: {
        ...(sovereignBankId && {
          OR: [
            { sourceSovereignBankId: sovereignBankId },
            { targetSovereignBankId: sovereignBankId },
          ],
        }),
        ...(status && { status }),
      },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
  }

  /**
   * Route interledger transaction
   */
  async routeInterledgerTransaction(
    sourceBankId: string,
    targetBankId: string,
    amount: string
  ): Promise<string[]> {
    // Determine optimal routing path
    // SCB → SCB (direct)
    // SCB → DBIS → SCB (via DBIS)
    // SCB → DBIS → Private Bank

    const routes: string[] = [];

    // Check if direct route exists
    const directRoute = await prisma.settlement_routes.findFirst({
      where: {
        sourceBankId,
        destinationBankId: targetBankId,
        status: 'active',
      },
    });

    if (directRoute) {
      routes.push('scb_to_scb');
    } else {
      // Route via DBIS
      routes.push('scb_to_dbis_to_scb');
    }

    return routes;
  }
}

export const cimInterledgerService = new CimInterledgerService();

