// DBIS Reporting Engine Service
// Generate consolidated statements, SCB reports

import { Decimal } from '@prisma/client/runtime/library';
import { Prisma } from '@prisma/client';
import { v4 as uuidv4 } from 'uuid';
import { accountService } from '@/core/accounts/account.service';
import { treasuryService } from '@/core/treasury/treasury.service';
import prisma from '@/shared/database/prisma';

export interface ConsolidatedStatementData {
  statementType: string;
  periodStart: Date;
  periodEnd: Date;
}

export interface SovereignReportData {
  sovereignBankId: string;
  reportType: string;
  reportPeriod: string;
  reportDate: Date;
}

export class ReportingEngineService {
  /**
   * Generate Consolidated Sovereign Liquidity Report (CSLR)
   */
  async generateCSLR(periodStart: Date, periodEnd: Date) {
    const banks = await prisma.sovereignBank.findMany({
      where: { status: 'active' },
      include: {
        liquidityPools: true,
        accounts: true,
      },
    });

    const consolidatedData: Record<string, unknown> = {
      periodStart,
      periodEnd,
      reportDate: new Date(),
      totalBanks: banks.length,
      liquidityByCurrency: {},
      totalLiquidity: 0,
      bankDetails: [],
    };

    for (const bank of banks) {
      const bankLiquidity = bank.liquidityPools.reduce(
        (sum, pool) => sum + parseFloat(pool.totalLiquidity.toString()),
        0
      );

      const lcr = await treasuryService.calculateLCR(bank.id);
      const nsfr = await treasuryService.calculateNSFR(bank.id);

      consolidatedData.bankDetails.push({
        sovereignBankId: bank.id,
        sovereignCode: bank.sovereignCode,
        name: bank.name,
        totalLiquidity: bankLiquidity,
        lcr,
        nsfr,
      });

      // Aggregate by currency
      for (const pool of bank.liquidityPools) {
        const currency = pool.currencyCode;
        if (!consolidatedData.liquidityByCurrency[currency]) {
          consolidatedData.liquidityByCurrency[currency] = 0;
        }
        consolidatedData.liquidityByCurrency[currency] += parseFloat(pool.totalLiquidity.toString());
      }

      consolidatedData.totalLiquidity += bankLiquidity;
    }

    const statement = await prisma.consolidatedStatement.create({
      data: {
        id: uuidv4(),
        statementId: uuidv4(),
        statementType: 'CSLR',
        reportDate: new Date(),
        periodStart,
        periodEnd,
        status: 'final',
        statementData: consolidatedData as Prisma.InputJsonValue,
        publishedAt: new Date(),
      },
    });

    return statement;
  }

  /**
   * Generate Cross-Border Settlement Exposures Report
   */
  async generateCrossBorderExposureReport(periodStart: Date, periodEnd: Date) {
    const settlements = await prisma.ledgerEntry.findMany({
      where: {
        timestampUtc: {
          gte: periodStart,
          lte: periodEnd,
        },
        status: 'settled',
      },
      include: {
        debitAccount: {
          include: {
            sovereignBank: true,
          },
        },
        creditAccount: {
          include: {
            sovereignBank: true,
          },
        },
      },
    });

    const exposures: Record<string, unknown> = {};
    const bankPairs: Record<string, number> = {};

    for (const settlement of settlements) {
      const debitBank = settlement.debitAccount.sovereignBank.sovereignCode;
      const creditBank = settlement.creditAccount.sovereignBank.sovereignCode;

      if (debitBank !== creditBank) {
        const pairKey = `${debitBank}_${creditBank}`;
        const amount = parseFloat(settlement.amount.toString());

        if (!bankPairs[pairKey]) {
          bankPairs[pairKey] = 0;
        }
        bankPairs[pairKey] += amount;

        // Track exposure by bank
        if (!exposures[debitBank]) {
          exposures[debitBank] = { outbound: 0, inbound: 0 };
        }
        if (!exposures[creditBank]) {
          exposures[creditBank] = { outbound: 0, inbound: 0 };
        }

        exposures[debitBank].outbound += amount;
        exposures[creditBank].inbound += amount;
      }
    }

    const reportData = {
      periodStart,
      periodEnd,
      reportDate: new Date(),
      totalCrossBorderSettlements: settlements.filter(
        (s) => s.debitAccount.sovereignBankId !== s.creditAccount.sovereignBankId
      ).length,
      exposures,
      bankPairs,
    };

    const statement = await prisma.consolidatedStatement.create({
      data: {
        id: uuidv4(),
        statementId: uuidv4(),
        statementType: 'CrossBorderExposure',
        reportDate: new Date(),
        periodStart,
        periodEnd,
        status: 'final',
        statementData: reportData as Prisma.InputJsonValue,
        publishedAt: new Date(),
      },
    });

    return statement;
  }

  /**
   * Generate CBDC Reserve Adequacy Statement
   */
  async generateCBDCReserveAdequacy(periodStart: Date, periodEnd: Date) {
    const cbdcIssuances = await prisma.cbdcIssuance.findMany({
      where: {
        timestampUtc: {
          gte: periodStart,
          lte: periodEnd,
        },
      },
      include: {
        sovereignBank: true,
      },
    });

    const adequacyData: Record<string, unknown> = {
      periodStart,
      periodEnd,
      reportDate: new Date(),
      totalIssuances: cbdcIssuances.length,
      bankAdequacy: [],
      totalCBDCIssued: 0,
      totalReserveBacking: 0,
    };

    for (const issuance of cbdcIssuances) {
      const bankIssuances = cbdcIssuances.filter(
        (i) => i.sovereignBankId === issuance.sovereignBankId
      );

      const totalIssued = bankIssuances.reduce(
        (sum, i) => sum + parseFloat(i.amountMinted.toString()),
        0
      );
      const totalBacking = bankIssuances.reduce(
        (sum, i) => sum + parseFloat(i.reserveBacking?.toString() || '0'),
        0
      );

      adequacyData.bankAdequacy.push({
        sovereignBankId: issuance.sovereignBankId,
        sovereignCode: issuance.sovereignBank.sovereignCode,
        totalCBDCIssued: totalIssued,
        totalReserveBacking: totalBacking,
        adequacyRatio: totalBacking > 0 ? totalIssued / totalBacking : 0,
      });

      adequacyData.totalCBDCIssued += totalIssued;
      adequacyData.totalReserveBacking += totalBacking;
    }

    const statement = await prisma.consolidatedStatement.create({
      data: {
        id: uuidv4(),
        statementId: uuidv4(),
        statementType: 'CBDCReserveAdequacy',
        reportDate: new Date(),
        periodStart,
        periodEnd,
        status: 'final',
        statementData: adequacyData as Prisma.InputJsonValue,
        publishedAt: new Date(),
      },
    });

    return statement;
  }

  /**
   * Generate SCB daily liquidity window report
   */
  async generateDailyLiquidityReport(sovereignBankId: string, reportDate: Date) {
    const lcr = await treasuryService.calculateLCR(sovereignBankId);
    const nsfr = await treasuryService.calculateNSFR(sovereignBankId);
    const accounts = await accountService.getAccountsBySovereign(sovereignBankId);

    const liquidityData = {
      reportDate,
      lcr,
      nsfr,
      totalAccounts: accounts.length,
      totalBalance: accounts.reduce((sum, acc) => sum + parseFloat(acc.balance), 0),
      availableBalance: accounts.reduce((sum, acc) => sum + parseFloat(acc.availableBalance), 0),
      reservedBalance: accounts.reduce((sum, acc) => sum + parseFloat(acc.reservedBalance), 0),
    };

    const report = await prisma.sovereignReport.create({
      data: {
        id: uuidv4(),
        sovereignBankId,
        reportId: uuidv4(),
        reportType: 'daily_liquidity',
        reportPeriod: 'daily',
        reportDate,
        dueDate: new Date(reportDate.getTime() + 24 * 60 * 60 * 1000), // Next day
        status: 'submitted',
        reportData: liquidityData,
        submittedAt: new Date(),
      },
    });

    return report;
  }

  /**
   * Generate SCB weekly FX reserve update
   */
  async generateWeeklyFXReserveReport(sovereignBankId: string, reportDate: Date) {
    const accounts = await accountService.getAccountsBySovereign(sovereignBankId);
    const fxReserves: Record<string, number> = {};

    for (const account of accounts) {
      if (account.assetType === 'fiat' || account.assetType === 'cbdc') {
        if (!fxReserves[account.currencyCode]) {
          fxReserves[account.currencyCode] = 0;
        }
        fxReserves[account.currencyCode] += parseFloat(account.balance);
      }
    }

    const report = await prisma.sovereignReport.create({
      data: {
        id: uuidv4(),
        sovereignBankId,
        reportId: uuidv4(),
        reportType: 'weekly_fx_reserve',
        reportPeriod: 'weekly',
        reportDate,
        dueDate: new Date(reportDate.getTime() + 7 * 24 * 60 * 60 * 1000), // Next week
        status: 'submitted',
        reportData: {
          reportDate,
          fxReserves,
          totalReserves: Object.values(fxReserves).reduce((sum, val) => sum + val, 0),
        },
        submittedAt: new Date(),
      },
    });

    return report;
  }

  /**
   * Generate SCB monthly AML compliance results
   */
  async generateMonthlyAMLComplianceReport(sovereignBankId: string, reportDate: Date) {
    const monthStart = new Date(reportDate.getFullYear(), reportDate.getMonth(), 1);
    const monthEnd = new Date(reportDate.getFullYear(), reportDate.getMonth() + 1, 0);

    const complianceRecords = await prisma.complianceRecord.findMany({
      where: {
        sovereignBankId,
        createdAt: {
          gte: monthStart,
          lte: monthEnd,
        },
      },
    });

    const reportData = {
      reportDate,
      monthStart,
      monthEnd,
      totalChecks: complianceRecords.length,
      clearCount: complianceRecords.filter((r) => r.status === 'clear').length,
      flaggedCount: complianceRecords.filter((r) => r.status === 'flagged').length,
      blockedCount: complianceRecords.filter((r) => r.status === 'blocked').length,
      averageRiskScore: complianceRecords.length > 0
        ? complianceRecords.reduce((sum, r) => sum + r.riskScore, 0) / complianceRecords.length
        : 0,
    };

    const report = await prisma.sovereignReport.create({
      data: {
        id: uuidv4(),
        sovereignBankId,
        reportId: uuidv4(),
        reportType: 'monthly_aml_compliance',
        reportPeriod: 'monthly',
        reportDate,
        dueDate: new Date(reportDate.getFullYear(), reportDate.getMonth() + 1, 15), // 15th of next month
        status: 'submitted',
        reportData,
        submittedAt: new Date(),
      },
    });

    return report;
  }

  /**
   * Generate SCB quarterly CBDC issuance audit
   */
  async generateQuarterlyCBDCAudit(sovereignBankId: string, reportDate: Date) {
    const quarterStart = new Date(reportDate.getFullYear(), Math.floor(reportDate.getMonth() / 3) * 3, 1);
    const quarterEnd = new Date(reportDate.getFullYear(), Math.floor(reportDate.getMonth() / 3) * 3 + 3, 0);

    const issuances = await prisma.cbdcIssuance.findMany({
      where: {
        sovereignBankId,
        timestampUtc: {
          gte: quarterStart,
          lte: quarterEnd,
        },
      },
    });

    const reportData = {
      reportDate,
      quarterStart,
      quarterEnd,
      totalIssuances: issuances.length,
      totalMinted: issuances.reduce((sum, i) => sum + parseFloat(i.amountMinted.toString()), 0),
      totalBurned: issuances.reduce((sum, i) => sum + parseFloat(i.amountBurned.toString()), 0),
      netChange: issuances.reduce((sum, i) => sum + parseFloat(i.netChange.toString()), 0),
      issuances: issuances.map((i) => ({
        recordId: i.recordId,
        operationType: i.operationType,
        amountMinted: parseFloat(i.amountMinted.toString()),
        amountBurned: parseFloat(i.amountBurned.toString()),
        reserveBacking: i.reserveBacking ? parseFloat(i.reserveBacking.toString()) : null,
        timestampUtc: i.timestampUtc,
      })),
    };

    const report = await prisma.sovereignReport.create({
      data: {
        id: uuidv4(),
        sovereignBankId,
        reportId: uuidv4(),
        reportType: 'quarterly_cbdc_audit',
        reportPeriod: 'quarterly',
        reportDate,
        dueDate: new Date(reportDate.getFullYear(), Math.floor(reportDate.getMonth() / 3) * 3 + 3, 15),
        status: 'submitted',
        reportData,
        submittedAt: new Date(),
      },
    });

    return report;
  }
}

export const reportingEngineService = new ReportingEngineService();

