// GRU Simulation Tables Service
// Generate and manage simulation tables for stress testing
// Implements tables from DBIS GRU Stress Framework

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 { gruStressAlgorithmsService } from './gru-stress-algorithms.service';


export interface PriceShockTableRow {
  xauShock: string;
  gruM00: string;
  gruM0: string;
  gruM1: string;
}

export interface BondStressTableRow {
  stressType: string;
  li99PpOsB10: string;
  li99PpAvB10: string;
  notes: string;
}

export interface RegionalReservePoolTableRow {
  region: string;
  commodityShock: string;
  fxShock: string;
  gruReserveImpact: string;
}

export class GruSimulationTablesService {
  /**
   * Generate GRU Price Shock Table
   * Maps XAU shock percentages to GRU M00/M0/M1 impacts
   */
  async generatePriceShockTable(
    xauShockPercent?: number
  ): Promise<PriceShockTableRow[]> {
    logger.info('GRU Simulation Tables: Generating price shock table');

    // Standard shock tiers from the document
    const shockTiers = xauShockPercent
      ? [{ percent: xauShockPercent, label: `+${xauShockPercent}%` }]
      : [
          { percent: 5, label: '+5%' },
          { percent: 15, label: '+15%' },
          { percent: 35, label: '+35%' },
          { percent: 70, label: '+70%' },
        ];

    const table: PriceShockTableRow[] = [];

    for (const tier of shockTiers) {
      const xauShock = new Decimal(tier.percent).dividedBy(100);
      
      // Calculate impacts using algorithm service
      const m00Impact = gruStressAlgorithmsService.calculateGruPriceShockImpact(
        xauShock,
        'M00'
      );
      const m0Impact = gruStressAlgorithmsService.calculateGruPriceShockImpact(
        xauShock,
        'M0'
      );
      const m1Impact = gruStressAlgorithmsService.calculateGruPriceShockImpact(
        xauShock,
        'M1'
      );

      table.push({
        xauShock: tier.label,
        gruM00: `+${m00Impact.times(100).toFixed(1)}%`,
        gruM0: `+${m0Impact.times(100).toFixed(1)}%`,
        gruM1: `+${m1Impact.times(100).toFixed(1)}%`,
      });
    }

    // Save to database
    const tableId = `SIM-TABLE-${uuidv4()}`;
    await prisma.gruStressSimulationTable.create({
      data: {
        tableId,
        tableType: 'price_shock',
        tableName: 'GRU Price Shock Table (XAU Link)',
        tableData: table,
        parameters: { xauShockPercent },
        generatedAt: new Date(),
      },
    });

    logger.info('GRU Simulation Tables: Price shock table generated', {
      tableId,
      rows: table.length,
    });

    return table;
  }

  /**
   * Generate Bond Stress Table
   * Maps stress types to bond NAV impacts
   */
  async generateBondStressTable(
    stressType?: string
  ): Promise<BondStressTableRow[]> {
    logger.info('GRU Simulation Tables: Generating bond stress table');

    // Standard stress scenarios from the document
    const stressScenarios = stressType
      ? [{ type: stressType }]
      : [
          {
            type: 'metal_shock_s3',
            notes: 'Higher metal-demand bonds stabilize',
          },
          {
            type: 'fx_cascade_f4',
            notes: 'FX volatility increases discounting',
          },
          {
            type: 'loop_instability',
            notes: 'Liquidity loop becomes unbalanced',
          },
          {
            type: 'sovereign_default',
            notes: 'Supranational reserves absorb shock',
          },
        ];

    const table: BondStressTableRow[] = [];

    for (const scenario of stressScenarios) {
      const osB10Impact = gruStressAlgorithmsService.calculateBondNavImpact(
        scenario.type,
        'Li99PpOsB10'
      );
      const avB10Impact = gruStressAlgorithmsService.calculateBondNavImpact(
        scenario.type,
        'Li99PpAvB10'
      );

      table.push({
        stressType: this.formatStressTypeName(scenario.type),
        li99PpOsB10: `${osB10Impact.times(100).toFixed(0)}% NAV`,
        li99PpAvB10: `${avB10Impact.times(100).toFixed(0)}% NAV`,
        notes: (scenario as any).notes || '',
      });
    }

    // Save to database
    const tableId = `SIM-TABLE-${uuidv4()}`;
    await prisma.gruStressSimulationTable.create({
      data: {
        tableId,
        tableType: 'bond_stress',
        tableName: 'GRU Bond Stress Table',
        tableData: table,
        parameters: { stressType },
        generatedAt: new Date(),
      },
    });

    logger.info('GRU Simulation Tables: Bond stress table generated', {
      tableId,
      rows: table.length,
    });

    return table;
  }

  /**
   * Generate Regional Reserve Pool Table
   * Maps regional scenarios to reserve impacts
   */
  async generateRegionalReservePoolTable(
    region?: string
  ): Promise<RegionalReservePoolTableRow[]> {
    logger.info('GRU Simulation Tables: Generating regional reserve pool table');

    // Standard regional scenarios from the document
    const regionalScenarios = region
      ? [{ region, commodityShock: 'S2', fxShock: 'F2' }]
      : [
          { region: 'EU', commodityShock: 'S2', fxShock: 'F2' },
          { region: 'AU', commodityShock: 'S3', fxShock: 'F1' },
          { region: 'ASEAN', commodityShock: 'S1', fxShock: 'F3' },
          { region: 'GCC', commodityShock: 'S3', fxShock: 'F2' },
          { region: 'MERCOSUR', commodityShock: 'S4', fxShock: 'F4' },
        ];

    const table: RegionalReservePoolTableRow[] = [];

    for (const scenario of regionalScenarios) {
      const impact = gruStressAlgorithmsService.calculateRegionalReserveImpact(
        scenario.region,
        scenario.commodityShock,
        scenario.fxShock
      );

      table.push({
        region: scenario.region,
        commodityShock: scenario.commodityShock,
        fxShock: scenario.fxShock,
        gruReserveImpact: `${impact.times(100).toFixed(1)}%`,
      });
    }

    // Save to database
    const tableId = `SIM-TABLE-${uuidv4()}`;
    await prisma.gruStressSimulationTable.create({
      data: {
        tableId,
        tableType: 'regional_reserve_pool',
        tableName: 'Regional Reserve Pool Stress Table',
        tableData: table,
        parameters: { region },
        generatedAt: new Date(),
      },
    });

    logger.info('GRU Simulation Tables: Regional reserve pool table generated', {
      tableId,
      rows: table.length,
    });

    return table;
  }

  /**
   * Get simulation table by type
   */
  async getSimulationTable(
    tableType: string,
    parameters?: Record<string, unknown>
  ) {
    const where: any = { tableType, status: 'active' };
    
    if (parameters) {
      where.parameters = { path: ['$'], equals: parameters };
    }

    const table = await prisma.gruStressSimulationTable.findFirst({
      where,
      orderBy: { generatedAt: 'desc' },
    });

    return table;
  }

  /**
   * Get all simulation tables
   */
  async getAllSimulationTables(tableType?: string) {
    const where: any = { status: 'active' };
    if (tableType) {
      where.tableType = tableType;
    }

    return await prisma.gruStressSimulationTable.findMany({
      where,
      orderBy: { generatedAt: 'desc' },
    });
  }

  /**
   * Format stress type name for display
   */
  private formatStressTypeName(stressType: string): string {
    const names: Record<string, string> = {
      metal_shock_s3: 'Metal Shock S3',
      fx_cascade_f4: 'FX Cascade F4',
      loop_instability: 'Loop Instability',
      sovereign_default: 'Sovereign Default',
    };

    return names[stressType] || stressType;
  }
}

export const gruSimulationTablesService = new GruSimulationTablesService();

