// Sovereign Risk Index (SRI) Calculator Service
// Multi-factor SRI calculation (AAA-CCC scale)

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


export interface SRIInputData {
  inputCategory: string;
  inputType: string;
  inputValue: number;
  weight?: number;
  source?: string;
}

export interface SRICalculationResult {
  sriScore: number;
  sriRating: string;
  breakdown: Record<string, number>;
}

export class SRICalculatorService {
  /**
   * Calculate SRI for a sovereign bank
   */
  async calculateSRI(sovereignBankId: string): Promise<SRICalculationResult> {
    // Collect all input data
    const inputs = await this.collectInputs(sovereignBankId);

    // Calculate weighted score
    const breakdown: Record<string, number> = {};
    let totalScore = 0;
    let totalWeight = 0;

    for (const input of inputs) {
      const weight = input.weight || this.getDefaultWeight(input.inputType);
      const normalizedValue = this.normalizeInput(input.inputType, input.inputValue);
      const contribution = normalizedValue * weight;

      breakdown[input.inputType] = contribution;
      totalScore += contribution;
      totalWeight += weight;
    }

    // Normalize to 0-100 scale
    const sriScore = totalWeight > 0 ? (totalScore / totalWeight) * 100 : 0;
    const sriRating = this.getSRIRating(sriScore);

    return {
      sriScore,
      sriRating,
      breakdown,
    };
  }

  /**
   * Collect all SRI inputs
   */
  private async collectInputs(sovereignBankId: string): Promise<SRIInputData[]> {
    const inputs: SRIInputData[] = [];

    // Financial Stability Inputs
    const slcr = await this.calculateSLCR(sovereignBankId);
    inputs.push({
      inputCategory: 'financial_stability',
      inputType: 'SLCR',
      inputValue: slcr,
      source: 'treasury_service',
    });

    const nsfrS = await this.calculateNSFRS(sovereignBankId);
    inputs.push({
      inputCategory: 'financial_stability',
      inputType: 'NSFR_S',
      inputValue: nsfrS,
      source: 'treasury_service',
    });

    const crossBorderExposure = await this.calculateCrossBorderExposure(sovereignBankId);
    inputs.push({
      inputCategory: 'financial_stability',
      inputType: 'cross_border_exposure',
      inputValue: crossBorderExposure,
      source: 'ledger_service',
    });

    // FX & Commodity Inputs
    const fxVol30d = await this.calculateFXVolatility(sovereignBankId, 30);
    inputs.push({
      inputCategory: 'fx_commodity',
      inputType: 'fx_volatility_30d',
      inputValue: fxVol30d,
      source: 'fx_service',
    });

    const fxVol90d = await this.calculateFXVolatility(sovereignBankId, 90);
    inputs.push({
      inputCategory: 'fx_commodity',
      inputType: 'fx_volatility_90d',
      inputValue: fxVol90d,
      source: 'fx_service',
    });

    const commodityReserve = await this.calculateCommodityReserve(sovereignBankId);
    inputs.push({
      inputCategory: 'fx_commodity',
      inputType: 'commodity_reserve',
      inputValue: commodityReserve,
      source: 'commodity_service',
    });

    // Operational Inputs
    const settlementFailureRate = await this.calculateSettlementFailureRate(sovereignBankId);
    inputs.push({
      inputCategory: 'operational',
      inputType: 'settlement_failure_rate',
      inputValue: settlementFailureRate,
      source: 'ledger_service',
    });

    const amlCompliance = await this.calculateAMLComplianceScore(sovereignBankId);
    inputs.push({
      inputCategory: 'operational',
      inputType: 'aml_compliance',
      inputValue: amlCompliance,
      source: 'compliance_service',
    });

    const cyberDefense = await this.calculateCyberDefenseMaturity(sovereignBankId);
    inputs.push({
      inputCategory: 'operational',
      inputType: 'cyber_defense',
      inputValue: cyberDefense,
      source: 'security_service',
    });

    return inputs;
  }

  /**
   * Calculate Sovereign Liquidity Coverage Ratio (SLCR)
   */
  private async calculateSLCR(sovereignBankId: string): Promise<number> {
    const lcr = await treasuryService.calculateLCR(sovereignBankId);
    // Convert to risk score (higher LCR = lower risk)
    // LCR > 1.0 is good, so invert for risk scoring
    const lcrValue = parseFloat(lcr);
    return lcrValue > 0 ? Math.max(0, 100 - lcrValue * 50) : 100;
  }

  /**
   * Calculate Net Stable Funding Ratio - Sovereign (NSFR-S)
   */
  private async calculateNSFRS(sovereignBankId: string): Promise<number> {
    const nsfr = await treasuryService.calculateNSFR(sovereignBankId);
    // NSFR > 1.0 is good, so invert for risk scoring
    const nsfrValue = parseFloat(nsfr);
    return nsfrValue > 0 ? Math.max(0, 100 - nsfrValue * 50) : 100;
  }

  /**
   * Calculate cross-border exposure index
   */
  private async calculateCrossBorderExposure(sovereignBankId: string): Promise<number> {
    // Get cross-border transactions
    const transactions = await prisma.ledgerEntry.findMany({
      where: {
        OR: [
          {
            debitAccount: {
              sovereignBankId,
            },
          },
          {
            creditAccount: {
              sovereignBankId,
            },
          },
        ],
        status: 'settled',
      },
      include: {
        debitAccount: true,
        creditAccount: true,
      },
      take: 1000, // Sample
    });

    // Calculate exposure ratio
    const crossBorderCount = transactions.filter(
      (t) => t.debitAccount.sovereignBankId !== t.creditAccount.sovereignBankId
    ).length;

    const exposureRatio = transactions.length > 0 ? crossBorderCount / transactions.length : 0;
    // Higher exposure = higher risk (but normalized)
    return exposureRatio * 100;
  }

  /**
   * Calculate FX volatility
   */
  private async calculateFXVolatility(sovereignBankId: string, days: number): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - days);

    const trades = await prisma.fxTrade.findMany({
      where: {
        sovereignBankId,
        timestampUtc: {
          gte: cutoffDate,
        },
        status: 'settled',
      },
      orderBy: { timestampUtc: 'asc' },
    });

    if (trades.length < 2) {
      return 0; // No volatility data
    }

    // Calculate price volatility
    const prices = trades.map((t) => parseFloat(t.price.toString()));
    const returns = [];
    for (let i = 1; i < prices.length; i++) {
      returns.push((prices[i] - prices[i - 1]) / prices[i - 1]);
    }

    const mean = returns.reduce((a, b) => a + b, 0) / returns.length;
    const variance =
      returns.reduce((sum, r) => sum + Math.pow(r - mean, 2), 0) / returns.length;
    const volatility = Math.sqrt(variance) * 100; // Convert to percentage

    return volatility;
  }

  /**
   * Calculate commodity reserve coverage
   */
  private async calculateCommodityReserve(sovereignBankId: string): Promise<number> {
    const accounts = await accountService.getAccountsBySovereign(sovereignBankId);
    const commodityAccounts = accounts.filter((acc) => acc.assetType === 'commodity');
    const totalCommodityValue = commodityAccounts.reduce(
      (sum, acc) => sum + parseFloat(acc.balance),
      0
    );

    // Normalize (higher reserves = lower risk)
    // This is simplified - in production would compare against requirements
    return Math.max(0, 100 - totalCommodityValue / 1000000); // Normalize
  }

  /**
   * Calculate settlement failure rate
   */
  private async calculateSettlementFailureRate(sovereignBankId: string): Promise<number> {
    const totalSettlements = await prisma.ledgerEntry.count({
      where: {
        OR: [
          {
            debitAccount: {
              sovereignBankId,
            },
          },
          {
            creditAccount: {
              sovereignBankId,
            },
          },
        ],
        status: {
          in: ['settled', 'failed'],
        },
      },
    });

    const failedSettlements = await prisma.ledgerEntry.count({
      where: {
        OR: [
          {
            debitAccount: {
              sovereignBankId,
            },
          },
          {
            creditAccount: {
              sovereignBankId,
            },
          },
        ],
        status: 'failed',
      },
    });

    const failureRate = totalSettlements > 0 ? (failedSettlements / totalSettlements) * 100 : 0;
    return failureRate;
  }

  /**
   * Calculate AML compliance score
   */
  private async calculateAMLComplianceScore(sovereignBankId: string): Promise<number> {
    const recentRecords = await prisma.complianceRecord.findMany({
      where: {
        sovereignBankId,
        createdAt: {
          gte: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000), // Last 90 days
        },
      },
    });

    if (recentRecords.length === 0) {
      return 0; // No compliance issues
    }

    const flaggedCount = recentRecords.filter((r) => r.status === 'flagged').length;
    const blockedCount = recentRecords.filter((r) => r.status === 'blocked').length;

    // Higher flagged/blocked = higher risk
    const riskScore = (flaggedCount * 10 + blockedCount * 50) / recentRecords.length;
    return Math.min(100, riskScore);
  }

  /**
   * Calculate cyber defense maturity
   */
  private async calculateCyberDefenseMaturity(sovereignBankId: string): Promise<number> {
    // Check if quantum-safe crypto is enabled
    const identities = await prisma.sovereignIdentity.findMany({
      where: {
        sovereignBankId,
        isQuantumEnabled: true,
      },
    });

    // Simplified: quantum-enabled = better security = lower risk
    const quantumRatio = identities.length > 0 ? 1 : 0;
    return (1 - quantumRatio) * 50; // 0 if quantum-enabled, 50 if not
  }

  /**
   * Normalize input value to 0-100 risk scale
   */
  private normalizeInput(inputType: string, value: number): number {
    // Different normalization based on input type
    // For most metrics, higher value = higher risk
    // For some (like LCR, NSFR), we already inverted in calculation
    return Math.max(0, Math.min(100, value));
  }

  /**
   * Get default weight for input type
   */
  private getDefaultWeight(inputType: string): number {
    const weights: Record<string, number> = {
      SLCR: 15,
      NSFR_S: 15,
      cross_border_exposure: 10,
      fx_volatility_30d: 10,
      fx_volatility_90d: 10,
      commodity_reserve: 5,
      settlement_failure_rate: 15,
      aml_compliance: 10,
      cyber_defense: 10,
    };

    return weights[inputType] || 5;
  }

  /**
   * Get SRI rating from score
   */
  private getSRIRating(score: number): string {
    if (score <= 10) return 'AAA';
    if (score <= 20) return 'AA';
    if (score <= 30) return 'A';
    if (score <= 40) return 'BBB';
    if (score <= 50) return 'BB';
    if (score <= 60) return 'B';
    return 'CCC';
  }

  /**
   * Create SRI record with inputs
   */
  async createSRI(sovereignBankId: string): Promise<{ sriId: string; score: number; rating: string }> {
    const calculation = await this.calculateSRI(sovereignBankId);

    // Archive previous active SRI
    await prisma.sovereignRiskIndex.updateMany({
      where: {
        sovereignBankId,
        status: 'active',
      },
      data: {
        status: 'historical',
      },
    });

    // Create new SRI
    const sri = await prisma.sovereignRiskIndex.create({
      data: {
        id: uuidv4(),
        sovereignBankId,
        sriScore: new Decimal(calculation.sriScore),
        sriRating: calculation.sriRating,
        calculatedAt: new Date(),
        effectiveDate: new Date(),
        status: 'active',
        metadata: {
          breakdown: calculation.breakdown,
        },
      },
    });

    // Store inputs (simplified - would use collected inputs)
    // In production, would store all collected inputs

    return {
      sriId: sri.id,
      score: calculation.sriScore,
      rating: calculation.sriRating,
    };
  }
}

export const sriCalculatorService = new SRICalculatorService();

