// FX Engine - Pricing, Order Matching, Settlement

import { Decimal } from '@prisma/client/runtime/library';
import {
  FxOrder,
  FxTrade,
  FxTradeType,
  FxOrderType,
  FxSettlementMode,
} from '@/shared/types';
import { FX_PRICING_METHODS } from '@/shared/constants';
import { v4 as uuidv4 } from 'uuid';
import { DbisError, ErrorCode } from '@/shared/types';
import prisma from '@/shared/database/prisma';

export class FxService {
  /**
   * Submit FX order
   */
  async submitOrder(
    sovereignBankId: string,
    order: FxOrder
  ): Promise<{ tradeId: string; status: string }> {
    // Get or create FX pair
    const fxPair = await this.getOrCreateFxPair(order.pair);

    // Calculate price based on order type
    let price: string;
    if (order.orderType === FxOrderType.MARKET) {
      price = await this.getMarketPrice(order.pair);
    } else if (order.orderType === FxOrderType.LIMIT && order.limitPrice) {
      price = order.limitPrice;
    } else {
      throw new DbisError(ErrorCode.VALIDATION_ERROR, 'Invalid order type or missing limit price');
    }

    // Create FX trade
    const tradeId = `FX-${uuidv4()}`;
    const trade = await prisma.fxTrade.create({
      data: {
        tradeId,
        sovereignBankId,
        fxPairId: fxPair.id,
        baseCurrency: order.pair.split('/')[0],
        quoteCurrency: order.pair.split('/')[1],
        tradeType: FxTradeType.SPOT,
        quantity: new Decimal(order.amount),
        price: new Decimal(price),
        orderType: order.orderType,
        initiatorEntity: sovereignBankId,
        settlementMode: order.settlement,
        status: 'pending',
      },
    });

    return {
      tradeId: trade.tradeId,
      status: trade.status,
    };
  }

  /**
   * Execute FX trade
   */
  async executeTrade(tradeId: string): Promise<FxTrade> {
    const trade = await prisma.fxTrade.findUnique({
      where: { tradeId },
      include: { fxPair: true },
    });

    if (!trade) {
      throw new DbisError(ErrorCode.NOT_FOUND, 'FX trade not found');
    }

    // Update trade status
    const updatedTrade = await prisma.fxTrade.update({
      where: { tradeId },
      data: {
        status: 'executed',
        executedAt: new Date(),
      },
    });

    return this.mapToFxTrade(updatedTrade);
  }

  /**
   * Get market price for FX pair
   */
  async getMarketPrice(pair: string): Promise<string> {
    // In production, this would fetch from market data feeds
    // For now, return a mock price
    const [base, quote] = pair.split('/');
    
    // Mock pricing logic - in production, use VWAP, TWAP, or DBIS SCI
    const mockPrices: Record<string, string> = {
      'OMF/USD': '1.25',
      'USD/EUR': '0.92',
      'OMF/EUR': '1.15',
    };

    return mockPrices[pair] || '1.00';
  }

  /**
   * Calculate VWAP (Volume Weighted Average Price)
   */
  async calculateVWAP(pair: string, timeWindow: number = 3600): Promise<string> {
    // In production, this would calculate from trade history
    // For now, return mock value
    return await this.getMarketPrice(pair);
  }

  /**
   * Calculate TWAP (Time Weighted Average Price)
   */
  async calculateTWAP(pair: string, timeWindow: number = 3600): Promise<string> {
    // In production, this would calculate from price history
    // For now, return mock value
    return await this.getMarketPrice(pair);
  }

  /**
   * Get or create FX pair
   */
  private async getOrCreateFxPair(pairCode: string): Promise<any> {
    let fxPair = await prisma.fxPair.findUnique({
      where: { pairCode },
    });

    if (!fxPair) {
      const [baseCurrency, quoteCurrency] = pairCode.split('/');
      fxPair = await prisma.fxPair.create({
        data: {
          baseCurrency,
          quoteCurrency,
          pairCode,
          pricingMethod: FX_PRICING_METHODS.VWAP,
          status: 'active',
        },
      });
    }

    return fxPair;
  }

  /**
   * Map Prisma model to FxTrade type
   */
  private mapToFxTrade(trade: any): FxTrade {
    return {
      id: trade.id,
      tradeId: trade.tradeId,
      sovereignBankId: trade.sovereignBankId,
      fxPairId: trade.fxPairId,
      baseCurrency: trade.baseCurrency,
      quoteCurrency: trade.quoteCurrency,
      tradeType: trade.tradeType as FxTradeType,
      quantity: trade.quantity.toString(),
      price: trade.price.toString(),
      orderType: trade.orderType as FxOrderType,
      initiatorEntity: trade.initiatorEntity,
      counterpartyEntity: trade.counterpartyEntity || undefined,
      settlementMode: trade.settlementMode as FxSettlementMode,
      status: trade.status,
      timestampUtc: trade.timestampUtc,
    };
  }
}

export const fxService = new FxService();

