// Supervisory AI Service
// Liquidity shock probability modeling
// FX collapse risk assessment
// CBDC stress indicator monitoring
// Commodity reserve depletion alerts

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';


export interface LiquidityShockProbability {
  sovereignBankId: string;
  probability: Decimal;
  severity: string;
  factors: string[];
}

export interface FxCollapseRisk {
  currencyPair: string;
  riskScore: Decimal;
  probability: Decimal;
  factors: string[];
}

export interface CbdcStressIndicator {
  sovereignBankId: string;
  stressLevel: string;
  indicators: string[];
}

export interface CommodityReserveAlert {
  commodityType: string;
  reserveLevel: Decimal;
  alertLevel: string;
  depletionRate: Decimal;
}

export class SupervisoryAiService {
  /**
   * Model liquidity shock probability
   */
  async modelLiquidityShockProbability(
    sovereignBankId: string
  ): Promise<LiquidityShockProbability> {
    // Get liquidity metrics
    const liquidityScore = await prisma.liquidityScore.findFirst({
      where: {
        sovereignBankId,
      },
      orderBy: { calculatedAt: 'desc' },
    });

    // Get liquidity pools
    const liquidityPools = await prisma.liquidityPool.findMany({
      where: {
        sovereignBankId,
      },
    });

    // Get recent settlement volumes
    const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
    const settlements = await prisma.atomicSettlement.findMany({
      where: {
        OR: [
          { sourceBankId: sovereignBankId },
          { destinationBankId: sovereignBankId },
        ],
        createdAt: {
          gte: oneDayAgo,
        },
        status: 'settled',
      },
    });

    // Calculate factors
    const factors: string[] = [];
    let probability = new Decimal(0);

    // Factor 1: Low liquidity score
    if (liquidityScore && liquidityScore.score.lt(70)) {
      factors.push('Low liquidity score');
      probability = probability.plus(0.3);
    }

    // Factor 2: High settlement volume
    const settlementVolume = settlements.reduce(
      (sum, s) => sum.plus(s.amount),
      new Decimal(0)
    );

    if (settlementVolume.gt(1000000000)) {
      factors.push('High settlement volume');
      probability = probability.plus(0.2);
    }

    // Factor 3: Low buffer levels
    const totalLiquidity = liquidityPools.reduce(
      (sum, pool) => sum.plus(pool.totalLiquidity),
      new Decimal(0)
    );

    const availableLiquidity = liquidityPools.reduce(
      (sum, pool) => sum.plus(pool.availableLiquidity),
      new Decimal(0)
    );

    if (totalLiquidity.gt(0)) {
      const bufferRatio = availableLiquidity.div(totalLiquidity);
      if (bufferRatio.lt(0.2)) {
        factors.push('Low buffer ratio');
        probability = probability.plus(0.3);
      }
    }

    // Determine severity
    let severity = 'low';
    if (probability.gte(0.7)) {
      severity = 'critical';
    } else if (probability.gte(0.5)) {
      severity = 'high';
    } else if (probability.gte(0.3)) {
      severity = 'medium';
    }

    return {
      sovereignBankId,
      probability: Decimal.min(probability, new Decimal(1)),
      severity,
      factors,
    };
  }

  /**
   * Assess FX collapse risk
   */
  async assessFxCollapseRisk(
    currencyPair: string
  ): Promise<FxCollapseRisk> {
    // Get FX pair
    const fxPair = await prisma.fxPair.findFirst({
      where: {
        pairCode: currencyPair,
        status: 'active',
      },
    });

    if (!fxPair) {
      throw new Error('FX pair not found');
    }

    // Get recent trades
    const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
    const trades = await prisma.fxTrade.findMany({
      where: {
        fxPairId: fxPair.id,
        createdAt: {
          gte: oneDayAgo,
        },
        status: 'executed',
      },
      orderBy: { executedAt: 'desc' },
    });

    if (trades.length === 0) {
      return {
        currencyPair,
        riskScore: new Decimal(0),
        probability: new Decimal(0),
        factors: [],
      };
    }

    // Calculate factors
    const factors: string[] = [];
    let riskScore = new Decimal(0);

    // Factor 1: High volatility
    const prices = trades.map((t) => t.price);
    const avgPrice = prices.reduce((sum, p) => sum.plus(p), new Decimal(0))
      .div(prices.length);

    const volatility = prices
      .map((p) => p.minus(avgPrice).abs())
      .reduce((sum, diff) => sum.plus(diff), new Decimal(0))
      .div(prices.length);

    const volatilityRatio = volatility.div(avgPrice);

    if (volatilityRatio.gt(0.1)) {
      factors.push('High volatility');
      riskScore = riskScore.plus(0.3);
    }

    // Factor 2: Rapid price decline
    if (trades.length >= 2) {
      const latestPrice = trades[0].price;
      const previousPrice = trades[1].price;

      if (latestPrice.lt(previousPrice)) {
        const decline = previousPrice.minus(latestPrice).div(previousPrice);
        if (decline.gt(0.05)) {
          factors.push('Rapid price decline');
          riskScore = riskScore.plus(0.4);
        }
      }
    }

    // Factor 3: Low trading volume
    const totalVolume = trades.reduce(
      (sum, t) => sum.plus(t.quantity),
      new Decimal(0)
    );

    if (totalVolume.lt(1000000)) {
      factors.push('Low trading volume');
      riskScore = riskScore.plus(0.2);
    }

    // Calculate probability (higher risk score = higher probability)
    const probability = Decimal.min(riskScore, new Decimal(1));

    return {
      currencyPair,
      riskScore,
      probability,
      factors,
    };
  }

  /**
   * Monitor CBDC stress indicators
   */
  async monitorCbdcStress(
    sovereignBankId: string
  ): Promise<CbdcStressIndicator> {
    // Get CBDC issuance
    const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
    const issuances = await prisma.cbdcIssuance.findMany({
      where: {
        sovereignBankId,
        createdAt: {
          gte: oneDayAgo,
        },
      },
    });

    // Get CBDC wallets
    const wallets = await prisma.cbdcWallet.findMany({
      where: {
        sovereignBankId,
        status: 'active',
      },
    });

    // Calculate factors
    const indicators: string[] = [];
    let stressLevel = 'normal';

    // Factor 1: High burn rate
    const burns = issuances.filter((i) => i.operationType === 'burn');
    const mints = issuances.filter((i) => i.operationType === 'mint');

    if (burns.length > mints.length * 2) {
      indicators.push('High burn rate');
      stressLevel = 'elevated';
    }

    // Factor 2: Low wallet balances
    const totalBalance = wallets.reduce(
      (sum, w) => sum.plus(w.balance),
      new Decimal(0)
    );

    if (totalBalance.lt(1000000)) {
      indicators.push('Low wallet balances');
      stressLevel = 'high';
    }

    // Factor 3: High issuance volatility
    if (issuances.length > 100) {
      indicators.push('High issuance volatility');
      if (stressLevel === 'normal') {
        stressLevel = 'elevated';
      }
    }

    return {
      sovereignBankId,
      stressLevel,
      indicators,
    };
  }

  /**
   * Alert on commodity reserve depletion
   */
  async alertCommodityReserveDepletion(
    commodityType: string
  ): Promise<CommodityReserveAlert | null> {
    // Get reserve certificates
    const certificates = await prisma.commodityReserveCertificate.findMany({
      where: {
        commodityType: commodityType.toUpperCase(),
        verificationStatus: 'verified',
      },
    });

    if (certificates.length === 0) {
      return null;
    }

    // Calculate total reserves
    const totalReserves = certificates.reduce(
      (sum, cert) => sum.plus(cert.quantity),
      new Decimal(0)
    );

    // Get CDTs for this commodity
    const cdts = await prisma.commodityDigitalToken.findMany({
      where: {
        commodityType: commodityType.toUpperCase(),
        status: 'active',
      },
    });

    const totalIssued = cdts.reduce(
      (sum, cdt) => sum.plus(cdt.weight),
      new Decimal(0)
    );

    // Calculate reserve ratio
    const reserveRatio = totalIssued.gt(0)
      ? totalReserves.div(totalIssued)
      : new Decimal(1);

    // Calculate depletion rate (transactions in last 30 days)
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
    const transactions = await prisma.cdtTransaction.findMany({
      where: {
        cdt: {
          commodityType: commodityType.toUpperCase(),
        },
        createdAt: {
          gte: thirtyDaysAgo,
        },
        transactionType: 'burn',
      },
    });

    const depletionAmount = transactions.reduce(
      (sum, tx) => sum.plus(tx.amount),
      new Decimal(0)
    );

    const depletionRate = totalReserves.gt(0)
      ? depletionAmount.div(totalReserves)
      : new Decimal(0);

    // Determine alert level
    let alertLevel = 'normal';
    if (reserveRatio.lt(0.5)) {
      alertLevel = 'critical';
    } else if (reserveRatio.lt(0.75)) {
      alertLevel = 'high';
    } else if (reserveRatio.lt(0.9)) {
      alertLevel = 'elevated';
    }

    return {
      commodityType: commodityType.toUpperCase(),
      reserveLevel: reserveRatio,
      alertLevel,
      depletionRate,
    };
  }

  /**
   * Get all liquidity shock probabilities
   */
  async getAllLiquidityShockProbabilities(): Promise<LiquidityShockProbability[]> {
    const banks = await prisma.sovereignBank.findMany({
      where: {
        status: 'active',
      },
    });

    const probabilities = await Promise.all(
      banks.map((bank) =>
        this.modelLiquidityShockProbability(bank.id)
      )
    );

    return probabilities.filter((p) => p.probability.gt(0));
  }
}

export const supervisoryAiService = new SupervisoryAiService();

