// GRU Bond Stress Service
// Specialized stress testing for GRU bonds (Li99PpOsB10, Li99PpAvB10)

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';
import { gruSimulationTablesService } from './gru-simulation-tables.service';


export interface BondStressTestRequest {
  bondType: 'Li99PpOsB10' | 'Li99PpAvB10';
  bondId?: string;
  stressRegime: string;
  stressTier?: string;
  parameters?: Record<string, unknown>;
}

export interface BondStressTestResult {
  bondStressTestId: string;
  bondId: string;
  bondType: string;
  stressRegime: string;
  navImpact: Decimal;
  cashImpact?: Decimal;
  fairValueImpact?: Decimal;
  status: string;
}

export class GruBondStressService {
  /**
   * Run stress test on specific bond
   */
  async runBondStressTest(
    request: BondStressTestRequest
  ): Promise<BondStressTestResult> {
    logger.info('GRU Bond Stress: Running bond stress test', { request });

    // Get bond if bondId provided, otherwise use bondType
    let bond;
    if (request.bondId) {
      bond = await prisma.gruBond.findUnique({
        where: { bondId: request.bondId },
      });
      if (!bond) {
        throw new Error(`Bond not found: ${request.bondId}`);
      }
    } else {
      // Find a bond of the specified type
      bond = await prisma.gruBond.findFirst({
        where: {
          bondType: request.bondType,
          status: 'active',
        },
      });
      if (!bond) {
        throw new Error(`No active bond found of type: ${request.bondType}`);
      }
    }

    // Calculate NAV impact based on stress regime
    const navImpact = gruStressAlgorithmsService.calculateBondNavImpact(
      request.stressRegime,
      request.bondType
    );

    // Calculate cash impact for loop instability
    let cashImpact: Decimal | undefined;
    if (request.stressRegime === 'loop_instability') {
      cashImpact = request.bondType === 'Li99PpOsB10'
        ? new Decimal(-0.16)
        : new Decimal(-0.11);
    }

    // Calculate fair value impact for sovereign default
    let fairValueImpact: Decimal | undefined;
    if (request.stressRegime === 'sovereign_default') {
      fairValueImpact = request.bondType === 'Li99PpOsB10'
        ? new Decimal(-0.35)
        : new Decimal(-0.29);
    }

    // Create stress test record
    const bondStressTestId = `BOND-STRESS-${uuidv4()}`;
    const stressTest = await prisma.gruBondStressTest.create({
      data: {
        bondStressTestId,
        bondId: bond.id,
        bondType: request.bondType,
        stressRegime: request.stressRegime,
        stressTier: request.stressTier || null,
        navImpact,
        cashImpact: cashImpact || null,
        fairValueImpact: fairValueImpact || null,
        testParameters: request.parameters || {},
        status: 'completed',
        testedAt: new Date(),
      },
    });

    logger.info('GRU Bond Stress: Bond stress test completed', {
      bondStressTestId,
      bondType: request.bondType,
      navImpact: navImpact.toString(),
    });

    return {
      bondStressTestId,
      bondId: bond.id,
      bondType: request.bondType,
      stressRegime: request.stressRegime,
      navImpact,
      cashImpact,
      fairValueImpact,
      status: 'completed',
    };
  }

  /**
   * Calculate NAV impact for a specific bond under stress scenario
   */
  async calculateBondNavImpact(
    bondId: string,
    stressScenario: {
      stressType: string;
      stressTier?: string;
    }
  ): Promise<Decimal> {
    const bond = await prisma.gruBond.findUnique({
      where: { bondId },
    });

    if (!bond) {
      throw new Error(`Bond not found: ${bondId}`);
    }

    const navImpact = gruStressAlgorithmsService.calculateBondNavImpact(
      stressScenario.stressType,
      bond.bondType as 'Li99PpOsB10' | 'Li99PpAvB10'
    );

    return navImpact;
  }

  /**
   * Get bond stress table with all scenarios
   */
  async getBondStressTable(): Promise<any> {
    logger.info('GRU Bond Stress: Generating bond stress table');

    const table = await gruSimulationTablesService.generateBondStressTable();

    return {
      tableType: 'bond_stress',
      tableName: 'GRU Bond Stress Table',
      data: table,
    };
  }

  /**
   * Get stress test results for a bond
   */
  async getBondStressTests(bondId: string) {
    return await prisma.gruBondStressTest.findMany({
      where: { bondId },
      orderBy: { testedAt: 'desc' },
    });
  }

  /**
   * Get stress test results by bond type
   */
  async getBondStressTestsByType(bondType: 'Li99PpOsB10' | 'Li99PpAvB10') {
    return await prisma.gruBondStressTest.findMany({
      where: { bondType },
      orderBy: { testedAt: 'desc' },
    });
  }

  /**
   * Get stress test by ID
   */
  async getBondStressTest(bondStressTestId: string) {
    return await prisma.gruBondStressTest.findUnique({
      where: { bondStressTestId },
      include: {
        bond: true,
      },
    });
  }
}

export const gruBondStressService = new GruBondStressService();

