// M-RTGS Queue Manager
// Priority queue with 3 tiers, queue algorithm implementation
// priority = systemic_value + fx_cost_penalty + liquidity_weight + SRI_adjustment

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';


export interface MrtgsQueueRequest {
  paymentId: string;
  assetType: string;
  amount: string;
  currencyCode: string;
  sourceBankId: string;
  destinationBankId: string;
  paymentType: string;
}

export interface QueuePriority {
  tier: number;
  priorityScore: Decimal;
  queuePosition: number;
}

export class MrtgsQueueManagerService {
  /**
   * Add payment to M-RTGS queue
   */
  async addToQueue(request: MrtgsQueueRequest): Promise<{ queueId: string; priority: QueuePriority }> {
    const queueId = `MRTGS-QUEUE-${uuidv4()}`;

    // Calculate priority tier
    const tier = this.calculatePriorityTier(request);

    // Calculate priority score
    const priorityScore = await this.calculatePriorityScore(request, tier);

    // Get current queue position
    const queuePosition = await this.getQueuePosition(priorityScore, tier);

    // Create queue entry
    await prisma.mrtgs_queues.create({
      data: {
        queueId,
        paymentId: request.paymentId,
        priorityTier: tier,
        priorityScore,
        assetType: request.assetType,
        amount: new Decimal(request.amount),
        currencyCode: request.currencyCode,
        sourceBankId: request.sourceBankId,
        destinationBankId: request.destinationBankId,
        queuePosition,
        status: 'queued',
      },
    });

    logger.info(`M-RTGS: Added payment to queue`, {
      queueId,
      paymentId: request.paymentId,
      tier,
      priorityScore: priorityScore.toString(),
      queuePosition,
    });

    return {
      queueId,
      priority: {
        tier,
        priorityScore,
        queuePosition,
      },
    };
  }

  /**
   * Calculate priority tier
   * Tier 1: Sovereign & systemic transactions
   * Tier 2: Interbank transactions
   * Tier 3: Retail CBDC traffic during peak hours
   */
  private calculatePriorityTier(request: MrtgsQueueRequest): number {
    // Check if sovereign transaction
    if (request.paymentType === 'scb_to_scb' || request.paymentType === 'institutional') {
      return 1;
    }

    // Check if interbank
    if (request.paymentType === 'bank_to_bank') {
      return 2;
    }

    // Retail CBDC
    return 3;
  }

  /**
   * Calculate priority score
   * priority = systemic_value + fx_cost_penalty + liquidity_weight + SRI_adjustment
   */
  private async calculatePriorityScore(
    request: MrtgsQueueRequest,
    tier: number
  ): Promise<Decimal> {
    // Systemic value (higher for Tier 1)
    const systemicValue = tier === 1 ? new Decimal(100) : tier === 2 ? new Decimal(50) : new Decimal(10);

    // FX cost penalty (lower is better, so we subtract)
    const fxCostPenalty = await this.calculateFxCostPenalty(request);
    const fxPenalty = new Decimal(100).minus(fxCostPenalty);

    // Liquidity weight (higher liquidity = higher score)
    const liquidityWeight = await this.calculateLiquidityWeight(request);

    // SRI adjustment (lower SRI risk = higher score)
    const sriAdjustment = await this.calculateSriAdjustment(request);

    // Total priority score
    const priorityScore = systemicValue
      .plus(fxPenalty)
      .plus(liquidityWeight)
      .plus(sriAdjustment);

    return priorityScore;
  }

  /**
   * Calculate FX cost penalty (0-100, lower is better)
   */
  private async calculateFxCostPenalty(request: MrtgsQueueRequest): Promise<Decimal> {
    // Simplified: assume 0.1% FX cost for same currency, 0.5% for cross-currency
    // In production, query actual FX rates
    const fxCost = request.sourceBankId === request.destinationBankId
      ? new Decimal(0.001)
      : new Decimal(0.005);

    return fxCost.times(100); // Convert to percentage
  }

  /**
   * Calculate liquidity weight (0-100, higher is better)
   */
  private async calculateLiquidityWeight(request: MrtgsQueueRequest): Promise<Decimal> {
    // Check liquidity pools for the asset type
    // Simplified: assume 80% liquidity for fiat/CBDC, 60% for commodities
    const baseLiquidity = request.assetType === 'fiat' || request.assetType === 'cbdc'
      ? 80
      : 60;

    return new Decimal(baseLiquidity);
  }

  /**
   * Calculate SRI adjustment (0-100, lower risk = higher score)
   */
  private async calculateSriAdjustment(request: MrtgsQueueRequest): Promise<Decimal> {
    // Get SRI scores for source and destination banks
    // Simplified: assume default SRI risk of 20 (on 0-100 scale, lower is better)
    // Adjustment: 100 - SRI_risk
    const sriRisk = new Decimal(20);
    return new Decimal(100).minus(sriRisk);
  }

  /**
   * Get queue position based on priority score and tier
   */
  private async getQueuePosition(priorityScore: Decimal, tier: number): Promise<number> {
    // Count higher priority items in queue
    const higherPriorityCount = await prisma.mrtgs_queues.count({
      where: {
        status: 'queued',
        OR: [
          { priorityTier: { lt: tier } },
          {
            priorityTier: tier,
            priorityScore: { gt: priorityScore },
          },
        ],
      },
    });

    return higherPriorityCount + 1;
  }

  /**
   * Get next payment from queue for processing
   */
  async getNextFromQueue(): Promise<{ queueId: string; paymentId: string } | null> {
    const nextQueueItem = await prisma.mrtgs_queues.findFirst({
      where: { status: 'queued' },
      orderBy: [
        { priorityTier: 'asc' },
        { priorityScore: 'desc' },
        { queuedAt: 'asc' },
      ],
    });

    if (!nextQueueItem) {
      return null;
    }

    // Mark as processing
    await prisma.mrtgs_queues.update({
      where: { queueId: nextQueueItem.queueId },
      data: {
        status: 'processing',
        processedAt: new Date(),
      },
    });

    return {
      queueId: nextQueueItem.queueId,
      paymentId: nextQueueItem.paymentId,
    };
  }
}

export const mrtgsQueueManagerService = new MrtgsQueueManagerService();

