// GSDS Pricing Engine Service
// Pricing calculation: base_value + volatility_factor + collateral_ratio - liquidity_penalty + SRI_adjustment

import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { sriCalculatorService } from '@/core/risk/sri/sri-calculator.service';
import prisma from '@/shared/database/prisma';
import { logger } from '@/infrastructure/monitoring/logger';

export interface PricingRequest {
  derivativeId: string;
  baseValue: string;
  volatilityFactor?: string;
  collateralRatio?: string;
  liquidityPenalty?: string;
  sriAdjustment?: string;
  pricingSource?: string; // gql_state, msc_parameters, ai_liquidity_estimator
}

export interface PricingResult {
  pricingId: string;
  syntheticPrice: string;
  baseValue: string;
  volatilityFactor: string;
  collateralRatio: string;
  liquidityPenalty: string;
  sriAdjustment: string;
}

export class GsdsPricingService {
  /**
   * Calculate synthetic derivative price
   * Formula: base_value + volatility_factor + collateral_ratio - liquidity_penalty + SRI_adjustment
   */
  async calculatePrice(request: PricingRequest): Promise<PricingResult> {
    const derivative = await prisma.synthetic_derivatives.findUnique({
      where: { derivativeId: request.derivativeId },
    });

    if (!derivative) {
      throw new Error(`Derivative not found: ${request.derivativeId}`);
    }

    // Get base value
    const baseValue = new Decimal(request.baseValue);

    // Calculate volatility factor (default to 0 if not provided)
    let volatilityFactor = new Decimal(0);
    if (request.volatilityFactor) {
      volatilityFactor = new Decimal(request.volatilityFactor);
    } else {
      // Calculate from market data if not provided
      volatilityFactor = await this.calculateVolatilityFactor(derivative.underlyingAsset);
    }

    // Calculate collateral ratio
    let collateralRatio = new Decimal(0);
    if (request.collateralRatio) {
      collateralRatio = new Decimal(request.collateralRatio);
    } else {
      collateralRatio = await this.calculateCollateralRatio(request.derivativeId);
    }

    // Calculate liquidity penalty
    let liquidityPenalty = new Decimal(0);
    if (request.liquidityPenalty) {
      liquidityPenalty = new Decimal(request.liquidityPenalty);
    } else {
      liquidityPenalty = await this.calculateLiquidityPenalty(derivative.underlyingAsset);
    }

    // Calculate SRI adjustment
    let sriAdjustment = new Decimal(0);
    if (request.sriAdjustment) {
      sriAdjustment = new Decimal(request.sriAdjustment);
    } else {
      sriAdjustment = await this.calculateSRIAdjustment(derivative.party1BankId, derivative.party2BankId);
    }

    // Calculate synthetic price
    const syntheticPrice = baseValue
      .plus(volatilityFactor)
      .plus(collateralRatio)
      .minus(liquidityPenalty)
      .plus(sriAdjustment);

    // Save pricing record
    const pricingId = `GSDS-PRICE-${uuidv4()}`;
    await prisma.gsds_pricing_engine.create({
      data: {
        id: uuidv4(),
        pricingId,
        derivativeId: request.derivativeId,
        baseValue,
        volatilityFactor,
        collateralRatio,
        liquidityPenalty,
        sriAdjustment,
        syntheticPrice,
        pricingSource: request.pricingSource || 'ai_liquidity_estimator',
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    });

    return {
      pricingId,
      syntheticPrice: syntheticPrice.toString(),
      baseValue: baseValue.toString(),
      volatilityFactor: volatilityFactor.toString(),
      collateralRatio: collateralRatio.toString(),
      liquidityPenalty: liquidityPenalty.toString(),
      sriAdjustment: sriAdjustment.toString(),
    };
  }

  /**
   * Calculate volatility factor from market data
   */
  private async calculateVolatilityFactor(underlyingAsset: string): Promise<Decimal> {
    // In production, this would fetch real market volatility data
    // For now, return a default based on asset type
    const volatilityMap: Record<string, number> = {
      ssu: 0.02,
      cbdc: 0.01,
      commodity: 0.05,
      security: 0.03,
      fiat: 0.015,
      fx: 0.025,
    };

    return new Decimal(volatilityMap[underlyingAsset.toLowerCase()] || 0.02);
  }

  /**
   * Calculate collateral ratio from derivative collateral
   */
  private async calculateCollateralRatio(derivativeId: string): Promise<Decimal> {
    const collaterals = await prisma.synthetic_derivative_collaterals.findMany({
      where: {
        derivativeId,
        status: 'active',
      },
    });

    if (collaterals.length === 0) {
      return new Decimal(0);
    }

    // Sum of collateral valuations
    const totalCollateral = collaterals.reduce(
      (sum, col) => sum.plus(col.valuation),
      new Decimal(0)
    );

    const derivative = await prisma.synthetic_derivatives.findUnique({
      where: { derivativeId },
    });

    if (!derivative) {
      return new Decimal(0);
    }

    // Ratio of collateral to notional
    return totalCollateral.dividedBy(derivative.notionalAmount);
  }

  /**
   * Calculate liquidity penalty based on asset liquidity
   */
  private async calculateLiquidityPenalty(underlyingAsset: string): Promise<Decimal> {
    // In production, this would use AI-driven liquidity estimators
    // For now, return a default based on asset type
    const penaltyMap: Record<string, number> = {
      ssu: 0.001,
      cbdc: 0.0005,
      commodity: 0.002,
      security: 0.0015,
      fiat: 0.0003,
      fx: 0.001,
    };

    return new Decimal(penaltyMap[underlyingAsset.toLowerCase()] || 0.001);
  }

  /**
   * Calculate SRI adjustment from party SRI scores
   */
  private async calculateSRIAdjustment(
    party1BankId: string,
    party2BankId: string | null
  ): Promise<Decimal> {
    try {
      const sri1 = await sriCalculatorService.calculateSRI(party1BankId);
      const sri1Score = parseFloat(sri1.sriScore.toString());

      let sri2Score = 0;
      if (party2BankId) {
        const sri2 = await sriCalculatorService.calculateSRI(party2BankId);
        sri2Score = parseFloat(sri2.sriScore.toString());
      }

      // Average SRI score (higher = riskier, so negative adjustment)
      const avgSRI = (sri1Score + sri2Score) / (party2BankId ? 2 : 1);
      
      // Convert SRI (0-100) to adjustment factor (-0.05 to 0.05)
      const adjustment = new Decimal(avgSRI / 1000).negated();

      return adjustment;
    } catch (error) {
      // If SRI calculation fails, return zero adjustment
      logger.warn('SRI calculation failed', {
        error: error instanceof Error ? error.message : 'Unknown error',
        party1BankId,
        party2BankId,
      });
      return new Decimal(0);
    }
  }

  /**
   * Get latest pricing for a derivative
   */
  async getLatestPricing(derivativeId: string): Promise<PricingResult | null> {
    const pricing = await prisma.gsds_pricing_engine.findFirst({
      where: { derivativeId },
      orderBy: { calculatedAt: 'desc' },
    });

    if (!pricing) {
      return null;
    }

    return {
      pricingId: pricing.pricingId,
      syntheticPrice: pricing.syntheticPrice.toString(),
      baseValue: pricing.baseValue.toString(),
      volatilityFactor: pricing.volatilityFactor.toString(),
      collateralRatio: pricing.collateralRatio.toString(),
      liquidityPenalty: pricing.liquidityPenalty.toString(),
      sriAdjustment: pricing.sriAdjustment.toString(),
    };
  }
}

export const gsdsPricingService = new GsdsPricingService();

