// GRU Stress Test Service
// Comprehensive stress testing framework with tiered scenarios, simulation tables, and advanced overlays

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '@/infrastructure/monitoring/logger';
import { gruIndexService, GruIndexCode } from './gru-index.service';
import { gruLiquidityLoopService } from './gru-liquidity-loop.service';
import { gruStressAlgorithmsService } from './gru-stress-algorithms.service';
import { gruSimulationTablesService } from './gru-simulation-tables.service';
import { gruBondStressService } from './gru-bond-stress.service';
import { gruReservePoolStressService } from './gru-reserve-pool-stress.service';
import { gruTemporalStressService } from './gru-temporal-stress.service';
import { gruOmegaReconciliationService } from './gru-omega-reconciliation.service';
import { gruAdvancedOverlaysService } from './gru-advanced-overlays.service';


export type StressRegime =
  | 'metal_shock'
  | 'fx_cascade'
  | 'liquidity_grid_collapse'
  | 'gru_loop_instability'
  | 'sovereign_default_correlation'
  | 'temporal'
  | 'quantum'
  | 'metaverse'
  | 'peg_stress';

export type StressTier = 'S1' | 'S2' | 'S3' | 'S4' | 'F1' | 'F2' | 'F3' | 'F4';

export interface StressTestRequest {
  stressRegime: StressRegime;
  stressTier?: StressTier;
  sovereignBankId?: string;
  parameters?: Record<string, unknown>;
}

export interface StressTestResult {
  testId: string;
  testName: string;
  stressRegime: string;
  status: string;
  results: Array<{
    metricName: string;
    metricValue: Decimal;
    threshold?: Decimal;
    passed?: boolean;
    impactLevel?: string;
  }>;
}

export class GruStressTestService {
  /**
   * Run stress test for specified regime
   */
  async runStressTest(
    request: StressTestRequest
  ): Promise<StressTestResult> {
    logger.info('GRU Stress Test: Running stress test', { request });

    const testId = `GRU-STRESS-${uuidv4()}`;
    const testName = this.getTestName(request.stressRegime);

    // Create test record
    const test = await prisma.gru_stress_tests.create({
      data: {
        testId,
        testName,
        stressRegime: request.stressRegime,
        testType: 'standard',
        sovereignBankId: request.sovereignBankId || null,
        parameters: request.parameters || {},
        status: 'running',
      },
    });

    try {
      // Run stress test based on regime
      let results: Array<{
        metricName: string;
        metricValue: Decimal;
        threshold?: Decimal;
        passed?: boolean;
        impactLevel?: string;
      }> = [];

      switch (request.stressRegime) {
        case 'metal_shock':
          results = await this.runMetalShockTest(testId, {
            ...request.parameters,
            stressTier: request.stressTier,
          });
          break;
        case 'fx_cascade':
          results = await this.runFxCascadeTest(testId, {
            ...request.parameters,
            stressTier: request.stressTier,
          });
          break;
        case 'liquidity_grid_collapse':
          results = await this.runLiquidityGridCollapseTest(
            testId,
            request.parameters
          );
          break;
        case 'gru_loop_instability':
          results = await this.runGruLoopInstabilityTest(
            testId,
            request.parameters
          );
          break;
        case 'sovereign_default_correlation':
          results = await this.runSovereignDefaultCorrelationTest(
            testId,
            request.parameters
          );
          break;
        case 'temporal':
          results = await this.runTemporalStressTest(testId, request.parameters);
          break;
        case 'quantum':
          results = await this.runQuantumStressTest(testId, request.parameters);
          break;
        case 'metaverse':
          results = await this.runMetaverseStressTest(testId, request.parameters);
          break;
        case 'peg_stress':
          results = await this.runPegStressTest(testId, request.parameters);
          break;
        default:
          throw new Error(`Unknown stress regime: ${request.stressRegime}`);
      }

      // Save results
      for (const result of results) {
        await prisma.gru_stress_test_results.create({
          data: {
            resultId: `GRU-RESULT-${uuidv4()}`,
            testId,
            testName,
            stressRegime: request.stressRegime,
            metricName: result.metricName,
            metricValue: result.metricValue,
            threshold: result.threshold || null,
            passed: result.passed || null,
            impactLevel: result.impactLevel || null,
            details: result as any,
          },
        });
      }

      // Update test status
      await prisma.gru_stress_tests.update({
        where: { testId },
        data: {
          status: 'completed',
          completedAt: new Date(),
        },
      });

      logger.info('GRU Stress Test: Stress test completed', {
        testId,
        stressRegime: request.stressRegime,
        resultsCount: results.length,
      });

      return {
        testId,
        testName,
        stressRegime: request.stressRegime,
        status: 'completed',
        results,
      };
    } catch (error: any) {
      // Update test status to failed
      await prisma.gru_stress_tests.update({
        where: { testId },
        data: {
          status: 'failed',
          completedAt: new Date(),
        },
      });

      logger.error('GRU Stress Test: Stress test failed', {
        testId,
        error: error.message,
      });

      throw error;
    }
  }

  /**
   * Metal Shock Model
   * Tests impact of Gold or PGM volatility with tiered scenarios (S1-S4)
   */
  private async runMetalShockTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    // Get stress tier or default
    const stressTier = (parameters?.stressTier as StressTier) || 'S3';
    const shockPercent = this.getShockPercentForTier(stressTier, 'metal');
    const metalType = (parameters?.metalType as string) || 'GOLD'; // GOLD or PGM

    // Get current index value
    const indexCode: GruIndexCode = metalType === 'GOLD' ? 'LiXAU' : 'LiPMG';
    const index = await gruIndexService.getIndex(indexCode);
    if (!index) {
      throw new Error(`Index not found: ${indexCode}`);
    }

    const currentValue = index.currentValue;
    const shockedValue = currentValue.times(new Decimal(1).plus(shockPercent));

    // Calculate impact on GRU valuation
    const impact = shockedValue.minus(currentValue).dividedBy(currentValue);

    // Check threshold (50% change threshold)
    const threshold = new Decimal(0.5);
    const passed = impact.abs().lessThan(threshold);

    // Determine impact level
    let impactLevel = 'low';
    const impactPercent = impact.abs().times(100);
    if (impactPercent.greaterThan(30)) {
      impactLevel = 'critical';
    } else if (impactPercent.greaterThan(20)) {
      impactLevel = 'high';
    } else if (impactPercent.greaterThan(10)) {
      impactLevel = 'medium';
    }

    return [
      {
        metricName: 'metal_shock_impact',
        metricValue: impact,
        threshold,
        passed,
        impactLevel,
      },
      {
        metricName: 'current_index_value',
        metricValue: currentValue,
      },
      {
        metricName: 'shocked_index_value',
        metricValue: shockedValue,
      },
      {
        metricName: 'shock_percent',
        metricValue: new Decimal(shockPercent).times(100),
      },
    ];
  }

  /**
   * FX Cascade Model
   * Tests USD or EUR dislocation impact with tiered scenarios (F1-F4)
   */
  private async runFxCascadeTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    // Get stress tier or default
    const stressTier = (parameters?.stressTier as StressTier) || 'F3';
    const cascadePercent = this.getShockPercentForTier(stressTier, 'fx');
    const currency = (parameters?.currency as string) || 'USD'; // USD or EUR

    // Simulate FX cascade impact
    // In production, would fetch actual FX rates and calculate impact
    const fxImpact = new Decimal(cascadePercent);

    // Calculate impact on GRU through FX triangulation
    // GRU → XAU → FX → Target Currency
    const gruImpact = fxImpact.times(0.7); // 70% pass-through

    // Check threshold (30% impact threshold)
    const threshold = new Decimal(0.3);
    const passed = gruImpact.abs().lessThan(threshold);

    // Determine impact level
    let impactLevel = 'low';
    const impactPercent = gruImpact.abs().times(100);
    if (impactPercent.greaterThan(25)) {
      impactLevel = 'critical';
    } else if (impactPercent.greaterThan(15)) {
      impactLevel = 'high';
    } else if (impactPercent.greaterThan(8)) {
      impactLevel = 'medium';
    }

    return [
      {
        metricName: 'fx_cascade_impact',
        metricValue: gruImpact,
        threshold,
        passed,
        impactLevel,
      },
      {
        metricName: 'fx_shock_percent',
        metricValue: new Decimal(cascadePercent).times(100),
      },
      {
        metricName: 'currency',
        metricValue: new Decimal(0), // Placeholder
      },
    ];
  }

  /**
   * Liquidity Grid Collapse Model
   * Tests ID-SLG (Instant Settlement Liquidity Grid) stress
   */
  private async runLiquidityGridCollapseTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    const collapsePercent = (parameters?.collapsePercent as number) || 0.4; // 40% default collapse

    // Simulate liquidity grid collapse
    // In production, would fetch actual liquidity data
    const gridLiquidity = new Decimal(1000000); // Base liquidity
    const collapsedLiquidity = gridLiquidity.times(
      new Decimal(1).minus(collapsePercent)
    );

    const impact = collapsedLiquidity.dividedBy(gridLiquidity);

    // Check threshold (60% remaining liquidity threshold)
    const threshold = new Decimal(0.6);
    const passed = impact.greaterThan(threshold);

    // Determine impact level
    let impactLevel = 'low';
    const remainingPercent = impact.times(100);
    if (remainingPercent.lessThan(40)) {
      impactLevel = 'critical';
    } else if (remainingPercent.lessThan(60)) {
      impactLevel = 'high';
    } else if (remainingPercent.lessThan(75)) {
      impactLevel = 'medium';
    }

    return [
      {
        metricName: 'liquidity_collapse_impact',
        metricValue: impact,
        threshold,
        passed,
        impactLevel,
      },
      {
        metricName: 'grid_liquidity',
        metricValue: gridLiquidity,
      },
      {
        metricName: 'collapsed_liquidity',
        metricValue: collapsedLiquidity,
      },
      {
        metricName: 'collapse_percent',
        metricValue: new Decimal(collapsePercent).times(100),
      },
    ];
  }

  /**
   * GRU Loop Instability Model
   * Tests 7→10→9.55 malfunction
   */
  private async runGruLoopInstabilityTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    // Test GRU liquidity loop stability
    // Expected: 7 → 10 → 9.55 (after FX)
    const initialAmount = new Decimal(7);
    const expectedMintAmount = new Decimal(10);
    const expectedNetAmount = new Decimal(9.55);

    // Simulate loop execution
    const mintedAmount = initialAmount.times(new Decimal(10).dividedBy(7));
    const fxCost = mintedAmount.times(new Decimal(0.045)); // 4.5% FX cost
    const netAmount = mintedAmount.minus(fxCost);

    // Check if loop is stable
    const mintDeviation = mintedAmount.minus(expectedMintAmount).abs();
    const netDeviation = netAmount.minus(expectedNetAmount).abs();

    const threshold = new Decimal(0.1); // 10% deviation threshold
    const mintPassed = mintDeviation.dividedBy(expectedMintAmount).lessThan(threshold);
    const netPassed = netDeviation.dividedBy(expectedNetAmount).lessThan(threshold);
    const passed = mintPassed && netPassed;

    // Determine impact level
    let impactLevel = 'low';
    const maxDeviation = Decimal.max(
      mintDeviation.dividedBy(expectedMintAmount),
      netDeviation.dividedBy(expectedNetAmount)
    ).times(100);

    if (maxDeviation.greaterThan(15)) {
      impactLevel = 'critical';
    } else if (maxDeviation.greaterThan(10)) {
      impactLevel = 'high';
    } else if (maxDeviation.greaterThan(5)) {
      impactLevel = 'medium';
    }

    return [
      {
        metricName: 'loop_stability_check',
        metricValue: new Decimal(passed ? 1 : 0),
        threshold: new Decimal(1),
        passed,
        impactLevel,
      },
      {
        metricName: 'mint_deviation',
        metricValue: mintDeviation,
      },
      {
        metricName: 'net_deviation',
        metricValue: netDeviation,
      },
      {
        metricName: 'max_deviation_percent',
        metricValue: maxDeviation,
      },
    ];
  }

  /**
   * Sovereign Default Correlation Model
   * Tests correlation with sovereign defaults
   */
  private async runSovereignDefaultCorrelationTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    const defaultCount = (parameters?.defaultCount as number) || 3; // 3 defaults
    const correlationFactor = (parameters?.correlationFactor as number) || 0.5; // 50% correlation

    // Simulate sovereign default impact
    // Each default increases GRU risk
    const defaultRisk = new Decimal(defaultCount).times(correlationFactor);
    const gruRiskImpact = defaultRisk.dividedBy(10); // Scaled impact

    // Check threshold (30% risk threshold)
    const threshold = new Decimal(0.3);
    const passed = gruRiskImpact.lessThan(threshold);

    // Determine impact level
    let impactLevel = 'low';
    const riskPercent = gruRiskImpact.times(100);
    if (riskPercent.greaterThan(25)) {
      impactLevel = 'critical';
    } else if (riskPercent.greaterThan(15)) {
      impactLevel = 'high';
    } else if (riskPercent.greaterThan(8)) {
      impactLevel = 'medium';
    }

    return [
      {
        metricName: 'sovereign_default_correlation',
        metricValue: gruRiskImpact,
        threshold,
        passed,
        impactLevel,
      },
      {
        metricName: 'default_count',
        metricValue: new Decimal(defaultCount),
      },
      {
        metricName: 'correlation_factor',
        metricValue: new Decimal(correlationFactor).times(100),
      },
    ];
  }

  /**
   * Get shock percent for tier
   */
  private getShockPercentForTier(
    tier: StressTier,
    type: 'metal' | 'fx'
  ): number {
    if (type === 'metal') {
      const metalTiers: Record<string, number> = {
        S1: 0.05, // +5%
        S2: 0.15, // +15%
        S3: 0.35, // +35%
        S4: 0.70, // +70%
      };
      return metalTiers[tier] || 0.35;
    } else {
      const fxTiers: Record<string, number> = {
        F1: 0.015, // ±1.5%
        F2: 0.05, // ±5%
        F3: 0.12, // ±12%
        F4: 0.30, // ±30%
      };
      return fxTiers[tier] || 0.12;
    }
  }

  /**
   * Run temporal stress test
   */
  private async runTemporalStressTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    const temporalType = (parameters?.temporalType as string) || 'retrocausal';
    let matrix;

    if (temporalType === 'retrocausal') {
      matrix = await gruTemporalStressService.runRetrocausalStressTest(
        parameters || {}
      );
    } else if (temporalType === 'predictive') {
      matrix = await gruTemporalStressService.runPredictiveStressTest(
        parameters || {}
      );
    } else {
      matrix = await gruTemporalStressService.runOmegaLayerStabilityTest();
    }

    return [
      {
        metricName: 'temporal_stress',
        metricValue: matrix.deviationPercent || new Decimal(0),
        impactLevel: matrix.impact || 'medium',
      },
    ];
  }

  /**
   * Run quantum stress test
   */
  private async runQuantumStressTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    const quantumResult = await gruAdvancedOverlaysService.runQuantumStressOverlay(
      parameters || {}
    );

    return [
      {
        metricName: 'quantum_impact',
        metricValue: quantumResult.quantumImpact,
        impactLevel: 'medium',
      },
    ];
  }

  /**
   * Peg Stress Model (GRU M1 cISO(C/T))
   * Tests P1-P6: Normal Ops, +20% Mint, -20% Burn, 10% Redemption, Liquidity Shock, Ops Delay.
   * Acceptance: peg_deviation ≤ ±0.50%, recovery_time ≤ 24h, supply_recon_error = 0.
   */
  private async runPegStressTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    const testIdP = (parameters?.testId as string) || 'P1';
    const instrument = (parameters?.instrument as string) || 'cUSDC';
    const pegDeviation = new Decimal(
      (parameters?.pegDeviation as number) ?? 0
    );
    const recoveryTimeHours = new Decimal(
      (parameters?.recoveryTimeHours as number) ?? 0
    );
    const supplyReconError = new Decimal(
      (parameters?.supplyReconError as number) ?? 0
    );

    const pegThreshold = new Decimal(0.005);
    const recoveryThreshold = new Decimal(24);
    const supplyThreshold = new Decimal(0);

    const pegPassed = pegDeviation.abs().lessThanOrEqualTo(pegThreshold);
    const recoveryPassed = recoveryTimeHours.lessThanOrEqualTo(recoveryThreshold);
    const supplyPassed = supplyReconError.equals(supplyThreshold);

    const allPassed = pegPassed && recoveryPassed && supplyPassed;

    return [
      {
        metricName: 'peg_deviation',
        metricValue: pegDeviation,
        threshold: pegThreshold,
        passed: pegPassed,
        impactLevel: pegPassed ? 'low' : 'high',
      },
      {
        metricName: 'recovery_time_hours',
        metricValue: recoveryTimeHours,
        threshold: recoveryThreshold,
        passed: recoveryPassed,
        impactLevel: recoveryPassed ? 'low' : 'high',
      },
      {
        metricName: 'supply_recon_error',
        metricValue: supplyReconError,
        threshold: supplyThreshold,
        passed: supplyPassed,
        impactLevel: supplyPassed ? 'low' : 'critical',
      },
      {
        metricName: 'peg_stress_overall',
        metricValue: new Decimal(allPassed ? 1 : 0),
        threshold: new Decimal(1),
        passed: allPassed,
        impactLevel: allPassed ? 'low' : 'high',
      },
    ];
  }

  /**
   * Run metaverse stress test
   */
  private async runMetaverseStressTest(
    testId: string,
    parameters?: Record<string, unknown>
  ): Promise<
    Array<{
      metricName: string;
      metricValue: Decimal;
      threshold?: Decimal;
      passed?: boolean;
      impactLevel?: string;
    }>
  > {
    const metaverseResult =
      await gruAdvancedOverlaysService.runMetaverseEconomicShock(
        parameters || {}
      );

    return [
      {
        metricName: 'metaverse_impact',
        metricValue: metaverseResult.avatarLiquidityImpact || new Decimal(0),
        impactLevel: 'medium',
      },
    ];
  }

  /**
   * Generate GRU price shock table
   */
  async generateGruPriceShockTable(xauShockPercent?: number) {
    return await gruSimulationTablesService.generatePriceShockTable(
      xauShockPercent
    );
  }

  /**
   * Generate bond stress table
   */
  async generateBondStressTable(stressType?: string) {
    return await gruSimulationTablesService.generateBondStressTable(stressType);
  }

  /**
   * Generate regional reserve pool table
   */
  async generateRegionalReservePoolTable(region?: string) {
    return await gruSimulationTablesService.generateRegionalReservePoolTable(
      region
    );
  }

  /**
   * Run bond stress test
   */
  async runBondStressTest(
    bondType: 'Li99PpOsB10' | 'Li99PpAvB10',
    stressRegime: string,
    parameters?: Record<string, unknown>
  ) {
    return await gruBondStressService.runBondStressTest({
      bondType,
      stressRegime,
      parameters,
    });
  }

  /**
   * Run supranational reserve pool stress
   */
  async runSupranationalReservePoolStress(
    poolId: 'SR-1' | 'SR-2' | 'SR-3',
    stressScenario: Record<string, unknown>
  ) {
    return await gruReservePoolStressService.runReservePoolStress({
      poolId,
      ...stressScenario,
    });
  }

  /**
   * Run retrocausal stress test
   */
  async runRetrocausalStressTest(parameters: Record<string, unknown>) {
    return await gruTemporalStressService.runRetrocausalStressTest(parameters);
  }

  /**
   * Run predictive stress test
   */
  async runPredictiveStressTest(parameters: Record<string, unknown>) {
    return await gruTemporalStressService.runPredictiveStressTest(parameters);
  }

  /**
   * Run Ω-Layer stability test
   */
  async runOmegaLayerStabilityTest() {
    return await gruTemporalStressService.runOmegaLayerStabilityTest();
  }

  /**
   * Run metaverse economic shock
   */
  async runMetaverseEconomicShock(parameters: Record<string, unknown>) {
    return await gruAdvancedOverlaysService.runMetaverseEconomicShock(
      parameters
    );
  }

  /**
   * Run quantum stress overlay
   */
  async runQuantumStressOverlay(parameters: Record<string, unknown>) {
    return await gruAdvancedOverlaysService.runQuantumStressOverlay(parameters);
  }

  /**
   * Run temporal compression test
   */
  async runTemporalCompressionTest(parameters: Record<string, unknown>) {
    return await gruAdvancedOverlaysService.runTemporalCompressionTest(
      parameters
    );
  }

  /**
   * Reconcile post-stress with Ω-Layer
   */
  async reconcilePostStress(testId: string) {
    return await gruOmegaReconciliationService.reconcilePostStress(testId);
  }

  /**
   * Get test name
   */
  private getTestName(regime: StressRegime): string {
    const names: Record<StressRegime, string> = {
      metal_shock: 'Metal Shock Model (Gold/PGM Volatility)',
      fx_cascade: 'FX Cascade Model (USD/EUR Dislocation)',
      liquidity_grid_collapse: 'Liquidity Grid Collapse Model (ID-SLG Stress)',
      gru_loop_instability: 'GRU Loop Instability Model (7→10→9.55 Malfunction)',
      sovereign_default_correlation: 'Sovereign Default Correlation Model',
      temporal: 'Temporal Stress Model',
      quantum: 'Quantum Stress Overlay',
      metaverse: 'Metaverse Economic Shock',
      peg_stress: 'GRU M1 Peg Stress (cISO P1-P6)',
    };

    return names[regime];
  }

  /**
   * Get stress test by ID
   */
  async getStressTest(testId: string) {
    return await prisma.gru_stress_tests.findUnique({
      where: { testId },
      include: {
        results: {
          orderBy: { timestamp: 'desc' },
        },
      },
    });
  }

  /**
   * Get stress tests for a bank
   */
  async getStressTestsForBank(sovereignBankId: string, status?: string) {
    const where: any = { sovereignBankId };
    if (status) {
      where.status = status;
    }

    return await prisma.gru_stress_tests.findMany({
      where,
      include: {
        results: true,
      },
      orderBy: { createdAt: 'desc' },
    });
  }
}

export const gruStressTestService = new GruStressTestService();

