// GRU Stress Algorithms Service
// Core algorithmic models for stress testing
// Implements formulas from DBIS GRU Stress Framework

import { Decimal } from '@prisma/client/runtime/library';

export interface MetalShockResult {
  gruNew: Decimal;
  impact: Decimal;
  impactPercent: Decimal;
}

export interface FxCascadeResult {
  fxImpact: Decimal;
  gruImpact: Decimal;
  correlationImpact: Decimal;
}

export interface LiquidityLoopResult {
  loopOutcome: Decimal;
  cycles: number;
  deviation: Decimal;
}

export interface TemporalCompressionResult {
  temporalStress: Decimal;
  deviation: Decimal;
  compressionRatio: Decimal;
}

export class GruStressAlgorithmsService {
  /**
   * Metal Shock Model
   * Formula: GRU_new = GRU_base * (1 + metal_shock_factor * weight)
   */
  calculateMetalShockModel(
    gruBase: Decimal,
    metalShockFactor: Decimal,
    weight: Decimal
  ): MetalShockResult {
    // metal_shock_factor is the percentage shock (e.g., 0.05 for 5%)
    // weight is the sensitivity weight (typically 0.64 for M00, 0.36 for M0, 0.18 for M1)
    const impact = metalShockFactor.times(weight);
    const gruNew = gruBase.times(new Decimal(1).plus(impact));

    return {
      gruNew,
      impact,
      impactPercent: impact.times(100),
    };
  }

  /**
   * FX Cascade Model
   * Formula: f = FX_volatility + correlation_matrix * shock_vector
   */
  calculateFxCascadeModel(
    fxVolatility: Decimal,
    correlationMatrix: Record<string, Record<string, Decimal>>,
    shockVector: Record<string, Decimal>
  ): FxCascadeResult {
    // Calculate correlation-weighted shock
    let correlationImpact = new Decimal(0);
    
    for (const [currency, correlations] of Object.entries(correlationMatrix)) {
      const shock = shockVector[currency] || new Decimal(0);
      let weightedShock = new Decimal(0);
      
      for (const [targetCurrency, correlation] of Object.entries(correlations)) {
        const targetShock = shockVector[targetCurrency] || new Decimal(0);
        weightedShock = weightedShock.plus(correlation.times(targetShock));
      }
      
      correlationImpact = correlationImpact.plus(shock.times(weightedShock));
    }

    // FX impact combines volatility and correlation effects
    const fxImpact = fxVolatility.plus(correlationImpact);
    
    // GRU impact through FX triangulation (70% pass-through)
    const gruImpact = fxImpact.times(0.7);

    return {
      fxImpact,
      gruImpact,
      correlationImpact,
    };
  }

  /**
   * Liquidity Loop Instability Model
   * Formula: loop_outcome = (capital * 10/7 * 0.955) ^ cycles
   * Represents the 7→10→9.55 liquidity cycle
   */
  calculateLiquidityLoopInstability(
    capital: Decimal,
    cycles: number
  ): LiquidityLoopResult {
    // Expected cycle: 7 → 10 → 9.55
    // Multiplier: 10/7 = 1.42857...
    // After FX: 0.955 (4.5% FX cost)
    const multiplier = new Decimal(10).dividedBy(7);
    const fxAdjustment = new Decimal(0.955);
    const cycleMultiplier = multiplier.times(fxAdjustment);

    // Calculate outcome after N cycles
    let loopOutcome = capital;
    for (let i = 0; i < cycles; i++) {
      loopOutcome = loopOutcome.times(cycleMultiplier);
    }

    // Expected outcome for one cycle: capital * 10/7 * 0.955 = capital * 1.3643
    const expectedOutcome = capital.times(multiplier).times(fxAdjustment);
    const deviation = loopOutcome.minus(expectedOutcome.times(cycles));

    return {
      loopOutcome,
      cycles,
      deviation,
    };
  }

  /**
   * Temporal Compression Model
   * Formula: T_stress = | GRU(t+Δ) - GRU(t−Δ) |
   */
  calculateTemporalCompression(
    gruTPlusDelta: Decimal,
    gruTMinusDelta: Decimal
  ): TemporalCompressionResult {
    // Calculate absolute difference between future and past timelines
    const temporalStress = gruTPlusDelta.minus(gruTMinusDelta).abs();
    
    // Calculate deviation as percentage of average
    const average = gruTPlusDelta.plus(gruTMinusDelta).dividedBy(2);
    const deviation = average.gt(0)
      ? temporalStress.dividedBy(average).times(100)
      : new Decimal(0);

    // Compression ratio (how much the timelines have diverged)
    const compressionRatio = gruTMinusDelta.gt(0)
      ? gruTPlusDelta.dividedBy(gruTMinusDelta)
      : new Decimal(1);

    return {
      temporalStress,
      deviation,
      compressionRatio,
    };
  }

  /**
   * Calculate GRU price shock impact based on XAU shock
   * Uses tiered impact weights from simulation tables
   */
  calculateGruPriceShockImpact(
    xauShockPercent: Decimal,
    gruType: 'M00' | 'M0' | 'M1'
  ): Decimal {
    // Impact weights from simulation table:
    // M00: 64% of XAU shock (3.2% / 5% = 0.64)
    // M0: 36% of XAU shock (1.8% / 5% = 0.36)
    // M1: 14% of XAU shock (0.7% / 5% = 0.14)
    const weights: Record<string, Decimal> = {
      M00: new Decimal(0.64),
      M0: new Decimal(0.36),
      M1: new Decimal(0.14),
    };

    const weight = weights[gruType] || new Decimal(0);
    return xauShockPercent.times(weight);
  }

  /**
   * Calculate bond NAV impact based on stress type
   */
  calculateBondNavImpact(
    stressType: string,
    bondType: 'Li99PpOsB10' | 'Li99PpAvB10'
  ): Decimal {
    // Impact percentages from bond stress table
    const impacts: Record<string, Record<string, Decimal>> = {
      metal_shock_s3: {
        Li99PpOsB10: new Decimal(-0.12), // -12%
        Li99PpAvB10: new Decimal(-0.08), // -8%
      },
      fx_cascade_f4: {
        Li99PpOsB10: new Decimal(-0.28), // -28%
        Li99PpAvB10: new Decimal(-0.22), // -22%
      },
      loop_instability: {
        Li99PpOsB10: new Decimal(-0.16), // -16%
        Li99PpAvB10: new Decimal(-0.11), // -11%
      },
      sovereign_default: {
        Li99PpOsB10: new Decimal(-0.35), // -35%
        Li99PpAvB10: new Decimal(-0.29), // -29%
      },
    };

    return impacts[stressType]?.[bondType] || new Decimal(0);
  }

  /**
   * Calculate regional reserve pool impact
   */
  calculateRegionalReserveImpact(
    region: string,
    commodityShock: string,
    fxShock: string
  ): Decimal {
    // Impact percentages from regional reserve pool table
    const impacts: Record<string, Decimal> = {
      EU: new Decimal(-0.042), // -4.2%
      AU: new Decimal(-0.093), // -9.3%
      ASEAN: new Decimal(-0.064), // -6.4%
      GCC: new Decimal(-0.111), // -11.1%
      MERCOSUR: new Decimal(-0.197), // -19.7%
    };

    return impacts[region] || new Decimal(0);
  }
}

export const gruStressAlgorithmsService = new GruStressAlgorithmsService();

