// SIRE Routing Service
// Optimal route calculation: route = argmin(cost(FX) + risk(SRI) + liquidity_penalty + SSU_adjustment)
// Route types: SCB→SCB, SCB→DBIS→SCB, SCB→DBIS→Private Bank

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { sireMetricsService } from './sire-metrics.service';


export interface SireRoutingRequest {
  sourceBankId: string;
  destinationBankId: string;
  amount: string;
  currencyCode: string;
}

export class SireRoutingService {
  /**
   * Calculate optimal route
   * route = argmin(cost(FX) + risk(SRI) + liquidity_penalty + SSU_adjustment)
   */
  async calculateOptimalRoute(
    request: SireRoutingRequest
  ): Promise<string> {
    // 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 metrics = await sireMetricsService.calculateMetrics(
          route.routeId,
          request.sourceBankId,
          request.destinationBankId
        );

        // Calculate total cost: cost(FX) + risk(SRI) + liquidity_penalty + SSU_adjustment
        const fxCost = metrics.fxCost || new Decimal(0);
        const sriRisk = metrics.sriScore || new Decimal(0);
        const liquidityPenalty = metrics.liquidityPenalty || new Decimal(0);
        const ssuAdjustment = metrics.ssuAdjustment || new Decimal(0);

        const totalCost = fxCost.plus(sriRisk).plus(liquidityPenalty).plus(ssuAdjustment);

        return {
          routeId: route.routeId,
          totalCost,
          metrics,
        };
      })
    );

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

    // Save routing decision
    const decisionId = await this.saveRoutingDecision(
      request.sourceBankId,
      request.destinationBankId,
      optimalRoute.routeId,
      optimalRoute.totalCost,
      optimalRoute.metrics
    );

    return optimalRoute.routeId;
  }

  /**
   * Get possible routes
   */
  private async getPossibleRoutes(
    sourceBankId: string,
    destinationBankId: string,
    currencyCode: string
  ) {
    // Get direct routes
    const directRoutes = await prisma.settlementRoute.findMany({
      where: {
        sourceBankId,
        destinationBankId,
        currencyCode,
        status: 'active',
      },
    });

    // Get routes via DBIS
    const viaDbisRoutes = await prisma.settlementRoute.findMany({
      where: {
        OR: [
          {
            sourceBankId,
            destinationBankId: 'DBIS',
            currencyCode,
            status: 'active',
          },
          {
            sourceBankId: 'DBIS',
            destinationBankId,
            currencyCode,
            status: 'active',
          },
        ],
      },
    });

    // Combine routes
    const allRoutes = [...directRoutes, ...viaDbisRoutes];

    // If no routes exist, create default routes
    if (allRoutes.length === 0) {
      // Create direct route
      const directRouteId = `ROUTE-${sourceBankId}-${destinationBankId}-${uuidv4()}`;
      await prisma.settlementRoute.create({
        data: {
          routeId: directRouteId,
          sourceBankId,
          destinationBankId,
          currencyCode,
          routeType: 'direct',
          status: 'active',
        },
      });

      // Create via DBIS route
      const viaDbisRouteId = `ROUTE-${sourceBankId}-DBIS-${destinationBankId}-${uuidv4()}`;
      await prisma.settlementRoute.create({
        data: {
          routeId: viaDbisRouteId,
          sourceBankId,
          destinationBankId,
          currencyCode,
          routeType: 'via_intermediary',
          intermediaryBankIds: ['DBIS'] as any,
          status: 'active',
        },
      });

      return await this.getPossibleRoutes(sourceBankId, destinationBankId, currencyCode);
    }

    return allRoutes;
  }

  /**
   * Save routing decision
   */
  private async saveRoutingDecision(
    sourceBankId: string,
    destinationBankId: string,
    routeId: string,
    totalCost: Decimal,
    metrics: any
  ): Promise<string> {
    const decisionId = `SIRE-DEC-${uuidv4()}`;

    const decision = await prisma.sireRoutingDecision.create({
      data: {
        decisionId,
        sourceBankId,
        destinationBankId,
        routeId,
        routeType: 'scb_to_scb', // Will be determined by route
        optimalRoute: metrics as any,
        fxCost: metrics.fxCost || null,
        sriRiskScore: metrics.sriScore || null,
        liquidityPenalty: metrics.liquidityPenalty || null,
        ssuAdjustment: metrics.ssuAdjustment || null,
        totalCost,
        decisionTimestamp: new Date(),
      },
    });

    // Save metrics
    await sireMetricsService.saveMetrics(decisionId, metrics);

    return decision.decisionId;
  }

  /**
   * Get routing history
   */
  async getRoutingHistory(
    sourceBankId?: string,
    destinationBankId?: string,
    limit: number = 100
  ) {
    return await prisma.sireRoutingDecision.findMany({
      where: {
        ...(sourceBankId && { sourceBankId }),
        ...(destinationBankId && { destinationBankId }),
      },
      orderBy: { decisionTimestamp: 'desc' },
      take: limit,
      include: {
        metrics: true,
        route: true,
      },
    });
  }

  /**
   * Get optimal route decision
   */
  async getOptimalRoute(decisionId: string) {
    return await prisma.sireRoutingDecision.findUnique({
      where: { decisionId },
      include: {
        metrics: true,
        route: true,
      },
    });
  }
}

export const sireRoutingService = new SireRoutingService();

