// CASO Optimizer Service
// Optimization function: argmin(fx_cost + liquidity_penalty + volatility_risk + SRI_factor + SSU_cost)

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 { sireRoutingService } from '@/core/settlement/sire/sire-routing.service';
import { sriCalculatorService } from '@/core/risk/sri/sri-calculator.service';


export interface CasoOptimizationRequest {
  sourceBankId: string;
  destinationBankId: string;
  amount: string;
  currencyCode: string;
  assetType: string;
}

export interface CasoOptimizationResult {
  routeId: string;
  totalCost: Decimal;
  fxCost: Decimal;
  liquidityPenalty: Decimal;
  volatilityRisk: Decimal;
  sriFactor: Decimal;
  ssuCost: Decimal;
}

export class CasoOptimizerService {
  /**
   * Calculate optimal settlement route
   * optimal_route = argmin(fx_cost + liquidity_penalty + volatility_risk + SRI_factor + SSU_cost)
   */
  async optimizeRoute(request: CasoOptimizationRequest): Promise<CasoOptimizationResult> {
    const optimizationId = `CASO-OPT-${uuidv4()}`;

    // Get all possible routes
    const routes = await this.getPossibleRoutes(
      request.sourceBankId,
      request.destinationBankId,
      request.currencyCode
    );

    if (routes.length === 0) {
      throw new Error('No routes found');
    }

    // Calculate costs for each route
    const routeCosts = await Promise.all(
      routes.map(async (route) => {
        const costs = await this.calculateRouteCosts(route, request);

        return {
          routeId: route.routeId,
          ...costs,
          totalCost: costs.fxCost
            .plus(costs.liquidityPenalty)
            .plus(costs.volatilityRisk)
            .plus(costs.sriFactor)
            .plus(costs.ssuCost),
        };
      })
    );

    // Find optimal route (argmin)
    const optimalRoute = routeCosts.reduce((min, current) =>
      current.totalCost.lt(min.totalCost) ? current : min
    );

    // Save optimization result
    await prisma.casoRoute.create({
      data: {
        routeId: optimalRoute.routeId,
        sourceBankId: request.sourceBankId,
        destinationBankId: request.destinationBankId,
        currencyCode: request.currencyCode,
        assetType: request.assetType,
        fxCost: optimalRoute.fxCost,
        liquidityPenalty: optimalRoute.liquidityPenalty,
        volatilityRisk: optimalRoute.volatilityRisk,
        sriFactor: optimalRoute.sriFactor,
        ssuCost: optimalRoute.ssuCost,
        totalCost: optimalRoute.totalCost,
        routePath: routes.find((r) => r.routeId === optimalRoute.routeId)?.routePath || [],
        status: 'active',
      },
    });

    await prisma.casoOptimization.create({
      data: {
        optimizationId,
        routeId: optimalRoute.routeId,
        optimizationType: 'gpn_routing',
        inputParameters: request as unknown as Record<string, unknown>,
        optimizationResult: optimalRoute as unknown as Record<string, unknown>,
        status: 'applied',
        calculatedAt: new Date(),
        appliedAt: new Date(),
      },
    });

    logger.info('CASO: Route optimized', {
      optimizationId,
      routeId: optimalRoute.routeId,
      totalCost: optimalRoute.totalCost.toString(),
    });

    return optimalRoute;
  }

  /**
   * Get possible routes
   */
  private async getPossibleRoutes(
    sourceBankId: string,
    destinationBankId: string,
    currencyCode: string
  ): Promise<Array<{ routeId: string; routePath: string[] }>> {
    // Use SIRE to get routes
    try {
      const sireRouteId = await sireRoutingService.calculateOptimalRoute({
        sourceBankId,
        destinationBankId,
        amount: '0', // Amount not needed for route discovery
        currencyCode,
      });

      // Get route details
      const route = await prisma.settlementRoute.findUnique({
        where: { routeId: sireRouteId },
      });

      if (route) {
        return [
          {
            routeId: route.routeId,
            routePath: (route.routePath as unknown as string[]) || [sourceBankId, destinationBankId],
          },
        ];
      }
    } catch (error) {
      logger.warn('SIRE routing unavailable, using default routes', { error });
    }

    // Fallback: direct route
    return [
      {
        routeId: `DIRECT-${sourceBankId}-${destinationBankId}`,
        routePath: [sourceBankId, destinationBankId],
      },
    ];
  }

  /**
   * Calculate route costs
   */
  private async calculateRouteCosts(
    route: { routeId: string; routePath: string[] },
    request: CasoOptimizationRequest
  ): Promise<{
    fxCost: Decimal;
    liquidityPenalty: Decimal;
    volatilityRisk: Decimal;
    sriFactor: Decimal;
    ssuCost: Decimal;
  }> {
    // FX cost
    const fxCost = await this.calculateFxCost(route, request);

    // Liquidity penalty
    const liquidityPenalty = await this.calculateLiquidityPenalty(route, request);

    // Volatility risk
    const volatilityRisk = await this.calculateVolatilityRisk(route, request);

    // SRI factor
    const sriFactor = await this.calculateSriFactor(route, request);

    // SSU cost
    const ssuCost = await this.calculateSsuCost(route, request);

    return {
      fxCost,
      liquidityPenalty,
      volatilityRisk,
      sriFactor,
      ssuCost,
    };
  }

  /**
   * Calculate FX cost
   */
  private async calculateFxCost(
    route: { routeId: string; routePath: string[] },
    request: CasoOptimizationRequest
  ): Promise<Decimal> {
    // Simplified: 0.1% for same currency, 0.5% for cross-currency
    const amount = new Decimal(request.amount);
    const fxRate = request.sourceBankId === request.destinationBankId ? 0.001 : 0.005;
    return amount.times(fxRate);
  }

  /**
   * Calculate liquidity penalty
   */
  private async calculateLiquidityPenalty(
    route: { routeId: string; routePath: string[] },
    request: CasoOptimizationRequest
  ): Promise<Decimal> {
    // Check liquidity availability
    // Simplified: assume 80% liquidity = 0 penalty, lower liquidity = higher penalty
    const liquidityScore = 80; // In production, query actual liquidity
    const penalty = new Decimal(100).minus(liquidityScore).div(100);
    return new Decimal(request.amount).times(penalty);
  }

  /**
   * Calculate volatility risk
   */
  private async calculateVolatilityRisk(
    route: { routeId: string; routePath: string[] },
    request: CasoOptimizationRequest
  ): Promise<Decimal> {
    // Simplified: assume 2% volatility risk
    const volatility = 0.02;
    return new Decimal(request.amount).times(volatility);
  }

  /**
   * Calculate SRI factor
   */
  private async calculateSriFactor(
    route: { routeId: string; routePath: string[] },
    request: CasoOptimizationRequest
  ): Promise<Decimal> {
    // Get SRI for destination bank
    try {
      const sriResult = await sriCalculatorService.calculateSRI(request.destinationBankId);
      const sriScore = new Decimal(sriResult.sriScore);
      // Convert SRI (0-100) to cost factor (0-1)
      return new Decimal(request.amount).times(sriScore.div(100));
    } catch (error) {
      logger.warn('SRI calculation failed, using default', { error });
      return new Decimal(request.amount).times(0.2); // Default 20% risk
    }
  }

  /**
   * Calculate SSU cost
   */
  private async calculateSsuCost(
    route: { routeId: string; routePath: string[] },
    request: CasoOptimizationRequest
  ): Promise<Decimal> {
    // SSU cost if using SSU for settlement
    // Simplified: 0.05% if cross-sovereign
    if (route.routePath.length > 2) {
      return new Decimal(request.amount).times(0.0005);
    }
    return new Decimal(0);
  }
}

export const casoOptimizerService = new CasoOptimizerService();

