// ISN Routing Service
// Dynamic routing (liquidity proximity, trust weighting, FX cost)

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';


export interface RouteOptions {
  sourceBankId: string;
  destinationBankId: string;
  currencyCode: string;
  amount: number;
  priority?: 'speed' | 'cost' | 'trust';
}

export interface Route {
  routeId: string;
  path: string[];
  estimatedLatency: number;
  totalCost: number;
  trustScore: number;
}

export class ISNRoutingService {
  /**
   * Find optimal route for settlement
   */
  async findOptimalRoute(options: RouteOptions): Promise<Route> {
    const { sourceBankId, destinationBankId, currencyCode, amount, priority = 'speed' } = options;

    // Check for direct route
    const directRoute = await this.findDirectRoute(sourceBankId, destinationBankId, currencyCode);
    if (directRoute) {
      return directRoute;
    }

    // Find routes via intermediaries
    const routes = await this.findRoutesViaIntermediaries(
      sourceBankId,
      destinationBankId,
      currencyCode,
      amount
    );

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

    // Score routes based on priority
    const scoredRoutes = routes.map((route) => ({
      ...route,
      score: this.scoreRoute(route, priority),
    }));

    // Return best route
    scoredRoutes.sort((a, b) => b.score - a.score);
    return scoredRoutes[0];
  }

  /**
   * Find direct route
   */
  private async findDirectRoute(
    sourceBankId: string,
    destinationBankId: string,
    currencyCode: string
  ): Promise<Route | null> {
    const route = await prisma.settlementRoute.findFirst({
      where: {
        sourceBankId,
        destinationBankId,
        currencyCode,
        status: 'active',
      },
    });

    if (!route) {
      return null;
    }

    return {
      routeId: route.routeId,
      path: [sourceBankId, destinationBankId],
      estimatedLatency: route.estimatedLatency || 50, // Default 50ms for direct
      totalCost: parseFloat(route.fxCost?.toString() || '0'),
      trustScore: parseFloat(route.trustWeight?.toString() || '1'),
    };
  }

  /**
   * Find routes via intermediaries
   */
  private async findRoutesViaIntermediaries(
    sourceBankId: string,
    destinationBankId: string,
    currencyCode: string,
    amount: number
  ): Promise<Route[]> {
    // Get all active banks with liquidity in this currency
    const banks = await prisma.sovereignBank.findMany({
      where: {
        status: 'active',
        liquidityPools: {
          some: {
            currencyCode,
            availableLiquidity: {
              gte: new Decimal(amount),
            },
          },
        },
      },
    });

    const routes: Route[] = [];

    // Try one-hop routes
    for (const intermediary of banks) {
      if (intermediary.id === sourceBankId || intermediary.id === destinationBankId) {
        continue;
      }

      const route1 = await this.findDirectRoute(sourceBankId, intermediary.id, currencyCode);
      const route2 = await this.findDirectRoute(intermediary.id, destinationBankId, currencyCode);

      if (route1 && route2) {
        routes.push({
          routeId: uuidv4(),
          path: [sourceBankId, intermediary.id, destinationBankId],
          estimatedLatency: route1.estimatedLatency + route2.estimatedLatency,
          totalCost: parseFloat(route1.totalCost.toString()) + parseFloat(route2.totalCost.toString()),
          trustScore: Math.min(route1.trustScore, route2.trustScore),
        });
      }
    }

    return routes;
  }

  /**
   * Score route based on priority
   */
  private scoreRoute(route: Route, priority: string): number {
    switch (priority) {
      case 'speed':
        // Lower latency = higher score
        return 1000 / (route.estimatedLatency + 1);
      case 'cost':
        // Lower cost = higher score
        return 1000 / (route.totalCost + 1);
      case 'trust':
        // Higher trust = higher score
        return route.trustScore * 100;
      default:
        // Balanced scoring
        return (
          (1000 / (route.estimatedLatency + 1)) * 0.4 +
          (1000 / (route.totalCost + 1)) * 0.3 +
          route.trustScore * 100 * 0.3
        );
    }
  }

  /**
   * Create or update route
   */
  async createOrUpdateRoute(
    sourceBankId: string,
    destinationBankId: string,
    currencyCode: string,
    liquidityProximity?: number,
    trustWeight?: number,
    fxCost?: number,
    estimatedLatency?: number
  ) {
    const existing = await prisma.settlementRoute.findFirst({
      where: {
        sourceBankId,
        destinationBankId,
        currencyCode,
      },
    });

    if (existing) {
      return await prisma.settlementRoute.update({
        where: { id: existing.id },
        data: {
          liquidityProximity: liquidityProximity ? new Decimal(liquidityProximity) : undefined,
          trustWeight: trustWeight ? new Decimal(trustWeight) : undefined,
          fxCost: fxCost ? new Decimal(fxCost) : undefined,
          estimatedLatency,
          lastUsedAt: new Date(),
        },
      });
    }

    return await prisma.settlementRoute.create({
      data: {
        id: uuidv4(),
        routeId: uuidv4(),
        sourceBankId,
        destinationBankId,
        currencyCode,
        routeType: 'direct',
        liquidityProximity: liquidityProximity ? new Decimal(liquidityProximity) : undefined,
        trustWeight: trustWeight ? new Decimal(trustWeight) : undefined,
        fxCost: fxCost ? new Decimal(fxCost) : undefined,
        estimatedLatency: estimatedLatency || 50,
        status: 'active',
      },
    });
  }

  /**
   * Calculate liquidity proximity
   */
  async calculateLiquidityProximity(
    sourceBankId: string,
    destinationBankId: string,
    currencyCode: string
  ): Promise<number> {
    const sourcePool = await prisma.liquidityPool.findUnique({
      where: {
        sovereignBankId_currencyCode: {
          sovereignBankId: sourceBankId,
          currencyCode,
        },
      },
    });

    const destPool = await prisma.liquidityPool.findUnique({
      where: {
        sovereignBankId_currencyCode: {
          sovereignBankId: destinationBankId,
          currencyCode,
        },
      },
    });

    if (!sourcePool || !destPool) {
      return 0;
    }

    const sourceLiquidity = parseFloat(sourcePool.availableLiquidity.toString());
    const destLiquidity = parseFloat(destPool.availableLiquidity.toString());

    // Proximity = average of both pools (normalized)
    return (sourceLiquidity + destLiquidity) / 2;
  }

  /**
   * Calculate trust weight
   */
  async calculateTrustWeight(sourceBankId: string, destinationBankId: string): Promise<number> {
    // Get SRI scores for both banks
    const sourceSRI = await prisma.sovereignRiskIndex.findFirst({
      where: {
        sovereignBankId: sourceBankId,
        status: 'active',
      },
    });

    const destSRI = await prisma.sovereignRiskIndex.findFirst({
      where: {
        sovereignBankId: destinationBankId,
        status: 'active',
      },
    });

    // Lower SRI = higher trust
    const sourceTrust = sourceSRI ? 100 - parseFloat(sourceSRI.sriScore.toString()) : 50;
    const destTrust = destSRI ? 100 - parseFloat(destSRI.sriScore.toString()) : 50;

    // Average trust
    return (sourceTrust + destTrust) / 2;
  }
}

export const isnRoutingService = new ISNRoutingService();

