// CBDC Engine - Minting, Burning, Distribution

import { Decimal } from '@prisma/client/runtime/library';
import {
  CbdcOperationType,
  CbdcIssuance,
  AssetType,
} from '@/shared/types';
import { ledgerService } from '@/core/ledger/ledger.service';
import { accountService } from '@/core/accounts/account.service';
import { LedgerEntryType } from '@/shared/types';
import { v4 as uuidv4 } from 'uuid';
import { DbisError, ErrorCode } from '@/shared/types';
import { Prisma } from '@prisma/client';
import prisma from '@/shared/database/prisma';

export class CbdcService {
  /**
   * Mint CBDC
   */
  async mintCbdc(
    sovereignBankId: string,
    walletId: string | null,
    amount: string,
    operatorIdentity: string,
    reason?: string
  ): Promise<CbdcIssuance> {
    // Verify 1:1 reserve backing
    await this.verifyReserveBacking(sovereignBankId, amount);

    const recordId = `CBDC-MINT-${uuidv4()}`;

    // Get treasury account for the sovereign bank
    const treasuryAccount = await this.getTreasuryAccount(sovereignBankId, 'OMDC');

    // Create CBDC issuance record
    const issuance = await prisma.cbdc_issuance.create({
      data: {
        id: uuidv4(),
        recordId,
        sovereignBankId,
        walletId,
        amountMinted: new Decimal(amount),
        amountBurned: new Decimal(0),
        netChange: new Decimal(amount),
        operationType: CbdcOperationType.MINT as string,
        operatorIdentity,
        reserveBacking: new Decimal(amount), // 1:1 backing
        timestampUtc: new Date(),
        metadata: reason ? ({ reason } as Prisma.InputJsonValue) : Prisma.JsonNull,
      },
    });

    // Create ledger entry
    const ledgerId = `${sovereignBankId}-CBDC`;
    await ledgerService.postDoubleEntry(
      ledgerId,
      treasuryAccount.id, // Debit: Treasury account
      treasuryAccount.id, // Credit: CBDC wallet (simplified)
      amount,
      'OMDC',
      AssetType.CBDC,
      LedgerEntryType.TYPE_B, // CBDC issuance
      recordId,
      undefined,
      { operationType: 'mint', walletId, reason }
    );

    return this.mapToCbdcIssuance(issuance);
  }

  /**
   * Burn CBDC
   */
  async burnCbdc(
    sovereignBankId: string,
    walletId: string | null,
    amount: string,
    operatorIdentity: string,
    reason?: string
  ): Promise<CbdcIssuance> {
    const recordId = `CBDC-BURN-${uuidv4()}`;

    // Get treasury account
    const treasuryAccount = await this.getTreasuryAccount(sovereignBankId, 'OMDC');

    // Create CBDC issuance record
    const issuance = await prisma.cbdc_issuance.create({
      data: {
        id: uuidv4(),
        recordId,
        sovereignBankId,
        walletId,
        amountMinted: new Decimal(0),
        amountBurned: new Decimal(amount),
        netChange: new Decimal(amount).neg(),
        operationType: CbdcOperationType.BURN as string,
        operatorIdentity,
        timestampUtc: new Date(),
        metadata: reason ? ({ reason } as Prisma.InputJsonValue) : Prisma.JsonNull,
      },
    });

    // Create ledger entry
    const ledgerId = `${sovereignBankId}-CBDC`;
    await ledgerService.postDoubleEntry(
      ledgerId,
      treasuryAccount.id, // Debit: CBDC wallet
      treasuryAccount.id, // Credit: Treasury account (simplified)
      amount,
      'OMDC',
      AssetType.CBDC,
      LedgerEntryType.TYPE_B, // CBDC redemption
      recordId,
      undefined,
      { operationType: 'burn', walletId, reason }
    );

    return this.mapToCbdcIssuance(issuance);
  }

  /**
   * Verify 1:1 reserve backing
   */
  private async verifyReserveBacking(sovereignBankId: string, amount: string): Promise<void> {
    // Get reserve account
    const reserveAccount = await this.getTreasuryAccount(sovereignBankId, 'OMF');
    const reserveBalance = parseFloat(reserveAccount.balance);
    const mintAmount = parseFloat(amount);

    if (reserveBalance < mintAmount) {
      throw new DbisError(
        ErrorCode.VALIDATION_ERROR,
        'Insufficient reserve backing for CBDC minting'
      );
    }
  }

  /**
   * Get treasury account for a sovereign bank
   */
  private async getTreasuryAccount(sovereignBankId: string, currencyCode: string) {
    const accounts = await accountService.getAccountsBySovereign(sovereignBankId);
    const treasuryAccount = accounts.find(
      (acc) => acc.accountType === 'treasury' && acc.currencyCode === currencyCode
    );

    if (!treasuryAccount) {
      throw new DbisError(ErrorCode.NOT_FOUND, 'Treasury account not found');
    }

    return treasuryAccount;
  }

  /**
   * Map Prisma model to CbdcIssuance type
   */
  private mapToCbdcIssuance(issuance: any): CbdcIssuance {
    return {
      id: issuance.id,
      recordId: issuance.recordId,
      sovereignBankId: issuance.sovereignBankId,
      walletId: issuance.walletId || undefined,
      amountMinted: issuance.amountMinted.toString(),
      amountBurned: issuance.amountBurned.toString(),
      netChange: issuance.netChange.toString(),
      operationType: issuance.operationType as CbdcOperationType,
      operatorIdentity: issuance.operatorIdentity,
      reserveBacking: issuance.reserveBacking?.toString(),
      timestampUtc: issuance.timestampUtc,
    };
  }
}

export const cbdcService = new CbdcService();

