// GPN Layer 3: Finality Layer
// Atomic settlement achieved when SCB ledger posts, DBIS Master Ledger posts, and hash-lock matches

import { createHash } from 'crypto';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '@/infrastructure/monitoring/logger';
import { gssMasterLedgerService } from '@/core/settlement/gss/gss-master-ledger.service';
import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';

export interface FinalityRequest {
  paymentId: string;
  scbLedgerHash: string;
  dbisLedgerHash?: string;
}

export interface FinalityResult {
  lockId: string;
  hashLock: string;
  finalityStatus: 'pending' | 'settled' | 'failed';
  scbLedgerMatched: boolean;
  dbisLedgerMatched: boolean;
}

export class GpnFinalityService {
  /**
   * Create hash-lock for payment finality
   */
  async createHashLock(paymentId: string): Promise<string> {
    const payment = await prisma.gpnPayment.findUnique({
      where: { paymentId },
    });

    if (!payment) {
      throw new Error('Payment not found');
    }

    // Generate hash-lock from payment details
    const lockData = JSON.stringify({
      paymentId,
      sourceBankId: payment.sourceBankId,
      destinationBankId: payment.destinationBankId,
      amount: payment.amount.toString(),
      currencyCode: payment.currencyCode,
      timestamp: new Date().toISOString(),
    });

    const hashLock = createHash('sha256').update(lockData).digest('hex');

    // Create settlement lock record
    const lockId = `GPN-LOCK-${uuidv4()}`;
    await prisma.gpnSettlementLock.create({
      data: {
        lockId,
        paymentId,
        hashLock,
        lockStatus: 'pending',
        expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
      },
    });

    logger.info(`GPN Layer 3: Created hash-lock for payment`, { paymentId, lockId, hashLock });

    return hashLock;
  }

  /**
   * Verify finality: SCB ledger + DBIS ledger + hash-lock match
   */
  async verifyFinality(request: FinalityRequest): Promise<FinalityResult> {
    const payment = await prisma.gpnPayment.findUnique({
      where: { paymentId: request.paymentId },
      include: {
        settlementLocks: {
          where: { lockStatus: 'pending' },
          orderBy: { createdAt: 'desc' },
          take: 1,
        },
      },
    });

    if (!payment) {
      throw new Error('Payment not found');
    }

    if (payment.settlementLocks.length === 0) {
      throw new Error('No settlement lock found for payment');
    }

    const lock = payment.settlementLocks[0];

    // Verify SCB ledger hash matches
    const scbLedgerMatched = payment.scbLedgerHash === request.scbLedgerHash;

    // Get or verify DBIS Master Ledger hash
    let dbisLedgerMatched = false;
    if (request.dbisLedgerHash) {
      dbisLedgerMatched = payment.dbisLedgerHash === request.dbisLedgerHash;
    } else {
      // Post to DBIS Master Ledger if not already posted
      try {
        // Get or create settlement node
        const settlementNode = await prisma.sovereignSettlementNode.findFirst({
          where: { sovereignBankId: payment.sourceBankId },
        });

        if (settlementNode) {
          const dbisResult = await gssMasterLedgerService.postToMasterLedger({
            nodeId: settlementNode.nodeId,
            sourceBankId: payment.sourceBankId,
            destinationBankId: payment.destinationBankId,
            amount: payment.amount.toString(),
            currencyCode: payment.currencyCode,
            assetType: payment.assetType,
            sovereignSignature: '', // Would be provided in production
          });

          dbisLedgerMatched = dbisResult.dbisLedgerHash !== null && dbisResult.dbisLedgerHash !== '';
          if (dbisLedgerMatched) {
            await prisma.gpnPayment.update({
              where: { paymentId: request.paymentId },
              data: { dbisLedgerHash: dbisResult.dbisLedgerHash },
            });
          }
        }
      } catch (error) {
        logger.error('Failed to post to DBIS Master Ledger', { error, paymentId: request.paymentId });
      }
    }

    // Verify hash-lock matches
    const hashLockMatched = lock.hashLock === this.calculateHashLock(payment);

    // Finality achieved if all three conditions are met
    const finalityStatus =
      scbLedgerMatched && dbisLedgerMatched && hashLockMatched ? 'settled' : 'pending';

    if (finalityStatus === 'settled') {
      // Update payment and lock status
      await prisma.gpnPayment.update({
        where: { paymentId: request.paymentId },
        data: {
          layer3Status: 'settled',
          status: 'settled',
          scbLedgerHash: request.scbLedgerHash,
          dbisLedgerHash: request.dbisLedgerHash || payment.dbisLedgerHash,
        },
      });

      await prisma.gpnSettlementLock.update({
        where: { lockId: lock.lockId },
        data: {
          lockStatus: 'matched',
          matchedAt: new Date(),
          scbLedgerHash: request.scbLedgerHash,
          dbisLedgerHash: request.dbisLedgerHash || payment.dbisLedgerHash,
        },
      });

      logger.info(`GPN Layer 3: Finality achieved for payment`, {
        paymentId: request.paymentId,
        lockId: lock.lockId,
      });
    }

    return {
      lockId: lock.lockId,
      hashLock: lock.hashLock,
      finalityStatus,
      scbLedgerMatched,
      dbisLedgerMatched,
    };
  }

  /**
   * Calculate hash-lock from payment data
   */
  private calculateHashLock(payment: {
    paymentId: string;
    sourceBankId: string;
    destinationBankId: string;
    amount: Decimal;
    currencyCode: string;
  }): string {
    const lockData = JSON.stringify({
      paymentId: payment.paymentId,
      sourceBankId: payment.sourceBankId,
      destinationBankId: payment.destinationBankId,
      amount: payment.amount.toString(),
      currencyCode: payment.currencyCode,
    });

    return createHash('sha256').update(lockData).digest('hex');
  }
}

export const gpnFinalityService = new GpnFinalityService();

