// GSS Master Ledger Service
// Dual-ledger synchronization (SCB + DBIS)
// Multi-asset posting logic
// Sovereign signature validation
//
// REFACTORED: DBIS-first + outbox pattern
// - DBIS commits first (authoritative)
// - SCB sync happens async via outbox
// - No blocking on external SCB availability

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 prisma from '@/shared/database/prisma';
import { logger } from '@/infrastructure/monitoring/logger';

export interface GssMasterLedgerEntry {
  nodeId: string;
  sourceBankId: string;
  destinationBankId: string;
  amount: string;
  currencyCode: string;
  assetType: string;
  sovereignSignature?: string;
}

export interface DualLedgerResult {
  entryId: string;
  sovereignLedgerHash: string | null; // null initially, populated by async worker
  dbisLedgerHash: string;
  dualCommit: boolean; // false initially, true when SCB sync completes
}

export class GssMasterLedgerService {
  /**
   * Post to DBIS Master Ledger (authoritative) + queue SCB sync
   * 
   * Flow:
   * 1. Post to DBIS ledger atomically (SQL function)
   * 2. Create outbox event for SCB sync (same transaction)
   * 3. Create master ledger tracking record
   * 4. Return immediately (SCB sync happens async)
   * 
   * CRITICAL: All steps happen in a single DB transaction
   */
  async postToMasterLedger(
    entry: GssMasterLedgerEntry,
    referenceId?: string
  ): Promise<DualLedgerResult> {
    const entryId = `GSS-ENTRY-${uuidv4()}`;
    const refId = referenceId || `REF-${uuidv4()}`;

    logger.info('GssMasterLedgerService: Posting to master ledger', {
      entryId,
      referenceId: refId,
      sourceBankId: entry.sourceBankId,
      destinationBankId: entry.destinationBankId,
      amount: entry.amount,
    });

    // Validate sovereign signature if provided (before posting)
    if (entry.sovereignSignature) {
      await this.validateSovereignSignature(entry);
    }

    // Single transaction: DBIS post + outbox + master record
    return await prisma.$transaction(async (tx) => {
      // Step 1: Post to DBIS master ledger atomically (SQL function)
      const dbisResult = await this.postToDBISMasterLedgerAtomic(
        entry,
        refId,
        tx
      );

      // Step 2: Create outbox event for SCB synchronization
      const outboxId = await this.createDualLedgerOutboxEvent(
        entry,
        dbisResult.entryId,
        dbisResult.blockHash,
        refId,
        tx
      );

      // Step 3: Create master ledger tracking record
      const masterEntry = await tx.gssMasterLedger.create({
        data: {
          entryId,
          nodeId: entry.nodeId,
          sourceBankId: entry.sourceBankId,
          destinationBankId: entry.destinationBankId,
          amount: new Decimal(entry.amount),
          currencyCode: entry.currencyCode,
          assetType: entry.assetType,
          sovereignSignature: entry.sovereignSignature || null,
          dualLedgerCommit: false, // Will be true when SCB sync completes
          dbisLedgerHash: dbisResult.blockHash,
          sovereignLedgerHash: null, // Set when SCB sync completes
          status: 'DBIS_COMMITTED', // Explicit state
          committedAt: new Date(),
          settledAt: null, // Set when dual commit completes
        },
      });

      logger.info('GssMasterLedgerService: Master ledger entry created', {
        entryId: masterEntry.entryId,
        dbisLedgerHash: dbisResult.blockHash,
        outboxId,
      });

      return {
        entryId: masterEntry.entryId,
        sovereignLedgerHash: null, // Will be populated by async worker
        dbisLedgerHash: dbisResult.blockHash,
        dualCommit: false, // Will be true when SCB sync completes
      };
    });
  }

  /**
   * Post to DBIS ledger using atomic SQL function
   * Called within a transaction
   */
  private async postToDBISMasterLedgerAtomic(
    entry: GssMasterLedgerEntry,
    referenceId: string,
    tx: any // Prisma transaction client
  ): Promise<{ entryId: string; blockHash: string }> {
    const sourceAccounts = await tx.bank_accounts.findMany({
      where: {
        sovereignBankId: entry.sourceBankId,
        currencyCode: entry.currencyCode,
        assetType: entry.assetType,
        status: 'active',
      },
      take: 1,
    });

    const destAccounts = await tx.bank_accounts.findMany({
      where: {
        sovereignBankId: entry.destinationBankId,
        currencyCode: entry.currencyCode,
        assetType: entry.assetType,
        status: 'active',
      },
      take: 1,
    });

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

    // Use ledger posting module (calls atomic SQL function)
    const result = await ledgerPostingModule.postEntry({
      ledgerId: 'Master',
      debitAccountId: sourceAccounts[0].id,
      creditAccountId: destAccounts[0].id,
      amount: entry.amount,
      currencyCode: entry.currencyCode,
      assetType: entry.assetType,
      transactionType: 'Type_A',
      referenceId,
      metadata: { gssEntry: true, masterLedger: true },
    });

    return {
      entryId: result.entryId,
      blockHash: result.blockHash,
    };
  }

  /**
   * Create outbox event for SCB synchronization
   * Called within a transaction
   */
  private async createDualLedgerOutboxEvent(
    entry: GssMasterLedgerEntry,
    internalEntryId: string,
    dbisLedgerHash: string,
    referenceId: string,
    tx: any // Prisma transaction client
  ): Promise<string> {
    const outboxId = `OUTBOX-${uuidv4()}`;
    const payload = {
      sourceBankId: entry.sourceBankId,
      destinationBankId: entry.destinationBankId,
      amount: entry.amount,
      currencyCode: entry.currencyCode,
      assetType: entry.assetType,
      referenceId,
      dbisLedgerHash,
    };
    const payloadHash = createHash('sha256')
      .update(JSON.stringify(payload))
      .digest('hex');

    await tx.dual_ledger_outbox.create({
      data: {
        outboxId,
        internalEntryId,
        sovereignBankId: entry.sourceBankId,
        ledgerId: `Sovereign_${entry.sourceBankId}`,
        referenceId,
        payload: payload as any,
        payloadHash,
        status: 'QUEUED',
        attempts: 0,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    });

    logger.info('GssMasterLedgerService: Outbox event created', {
      outboxId,
      sovereignBankId: entry.sourceBankId,
      referenceId,
    });

    return outboxId;
  }

  /**
   * Validate sovereign signature
   */
  private async validateSovereignSignature(
    entry: GssMasterLedgerEntry
  ): Promise<boolean> {
    // In production, this would validate against HSM-stored keys
    // For now, we just verify the signature exists
    if (!entry.sovereignSignature) {
      return false;
    }

    // Get sovereign identity
    const identity = await prisma.sovereign_identities.findFirst({
      where: {
        sovereignBankId: entry.sourceBankId,
        identityType: 'Settlement',
        status: 'active',
      },
    });

    if (!identity) {
      throw new Error('Sovereign identity not found');
    }

    // Signature validation would happen here with HSM
    return true;
  }

  /**
   * Get master ledger entries
   */
  async getMasterLedgerEntries(
    nodeId?: string,
    status?: string,
    limit: number = 100
  ) {
    return await prisma.gss_master_ledger.findMany({
      where: {
        ...(nodeId && { nodeId }),
        ...(status && { status }),
      },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
  }

  /**
   * Get entry by ID
   */
  async getMasterLedgerEntry(entryId: string) {
    return await prisma.gss_master_ledger.findUnique({
      where: { entryId },
    });
  }

  /**
   * Verify dual-ledger synchronization
   */
  async verifyDualLedgerSync(entryId: string): Promise<boolean> {
    const entry = await prisma.gss_master_ledger.findUnique({
      where: { entryId },
    });

    if (!entry) {
      return false;
    }

    // Verify both ledger hashes exist
    if (!entry.sovereignLedgerHash || !entry.dbisLedgerHash) {
      return false;
    }

    // Verify both ledgers have matching entries
    const sovereignEntry = await prisma.ledger_entries.findFirst({
      where: { blockHash: entry.sovereignLedgerHash },
    });

    const dbisEntry = await prisma.ledger_entries.findFirst({
      where: { blockHash: entry.dbisLedgerHash },
    });

    return !!(sovereignEntry && dbisEntry && entry.dualLedgerCommit);
  }

  /**
   * Get outbox status for a master ledger entry
   */
  async getOutboxStatus(dbisLedgerHash: string): Promise<{
    status: string;
    scbLedgerHash: string | null;
    attempts: number;
    lastError: string | null;
  } | null> {
    const outbox = await prisma.dual_ledger_outbox.findFirst({
      where: {
        payload: {
          path: ['dbisLedgerHash'],
          equals: dbisLedgerHash,
        },
      },
    });

    if (!outbox) {
      return null;
    }

    return {
      status: outbox.status,
      scbLedgerHash: outbox.scbLedgerHash,
      attempts: outbox.attempts,
      lastError: outbox.lastError,
    };
  }
}

export const gssMasterLedgerService = new GssMasterLedgerService();

