// GRU/FX Service
// Real-time GRU/FX rate discovery, conversion, and history

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { DbisError, ErrorCode } from '@/shared/types';
import { fxService } from '@/core/fx/fx.service';
import {
  GruFxRate,
  GruFxRateHistory,
  GruFxConversionRequest,
  GruFxConversionResponse,
  SettlementAsset,
} from '../nostro-vostro.types';

export class GruFxService {
  /**
   * Get real-time GRU/FX rate
   */
  async getRate(pair: string): Promise<GruFxRate> {
    // Normalize pair format (e.g., "USD/GRU" or "GRU/USD")
    const [base, quote] = this.parsePair(pair);

    // Check if GRU is involved
    const isGruPair = base === 'GRU' || quote === 'GRU';

    if (!isGruPair) {
      // For non-GRU pairs, use standard FX service
      const rate = await fxService.getMarketPrice(pair);
      return {
        pair,
        rate,
        timestamp: new Date(),
        source: 'DBIS_FX',
      };
    }

    // For GRU pairs, calculate rate
    // If base is GRU, we need to get GRU value in quote currency
    // If quote is GRU, we need to get base currency value in GRU

    let rate: string;
    if (base === 'GRU') {
      // GRU/USD - get GRU value in USD
      rate = await this.getGruValueInCurrency(quote);
    } else {
      // USD/GRU - get currency value in GRU (inverse)
      const gruValue = await this.getGruValueInCurrency(base);
      rate = new Decimal(1).div(new Decimal(gruValue)).toString();
    }

    // Get bid/ask spread (typically 0.1% for GRU pairs)
    const spread = new Decimal(rate).mul(0.001);
    const bid = new Decimal(rate).minus(spread.div(2)).toString();
    const ask = new Decimal(rate).plus(spread.div(2)).toString();

    return {
      pair,
      rate,
      timestamp: new Date(),
      source: 'DBIS_GRU',
      bid,
      ask,
      spread: spread.toString(),
    };
  }

  /**
   * Get GRU value in a specific currency
   */
  private async getGruValueInCurrency(currency: string): Promise<string> {
    // In production, this would:
    // 1. Get GRU valuation from GRU service (XAU triangulation)
    // 2. Get currency/XAU rate
    // 3. Calculate GRU/currency rate

    // For now, use a simplified approach:
    // Get latest GRU valuation (would come from GRU service)
    // For demo, assume GRU = 1.5 USD (would be calculated from XAU)

    // Try to get from FX service if USD/currency pair exists
    if (currency === 'USD') {
      return '1.5'; // Mock GRU/USD rate
    }

    // Get USD/currency rate and convert
    try {
      const usdCurrencyRate = await fxService.getMarketPrice(`USD/${currency}`);
      const gruUsdRate = '1.5'; // Mock
      return new Decimal(gruUsdRate).mul(new Decimal(usdCurrencyRate)).toString();
    } catch {
      // Fallback: assume 1:1 if rate not available
      return '1.0';
    }
  }

  /**
   * Get rate history
   */
  async getRateHistory(
    pair: string,
    startDate: Date,
    endDate: Date,
    interval?: '1h' | '1d' | '1w'
  ): Promise<GruFxRateHistory> {
    // In production, this would query historical rate data
    // For now, generate mock data points

    const rates: Array<{ rate: string; timestamp: Date }> = [];
    const intervalMs = this.getIntervalMs(interval || '1d');

    let currentDate = new Date(startDate);
    while (currentDate <= endDate) {
      // Get rate for this timestamp (in production, from historical data)
      const rateData = await this.getRate(pair);
      rates.push({
        rate: rateData.rate,
        timestamp: new Date(currentDate),
      });

      currentDate = new Date(currentDate.getTime() + intervalMs);
    }

    return {
      pair,
      rates,
      startDate,
      endDate,
    };
  }

  /**
   * Convert currency using GRU/FX rates
   */
  async convert(request: GruFxConversionRequest): Promise<GruFxConversionResponse> {
    const conversionId = `CONV-${uuidv4()}`;
    const fromAmount = new Decimal(request.amount);

    // Determine conversion path
    let rate: string;
    let toAmount: Decimal;

    // If converting to/from GRU
    if (request.toCurrency === 'GRU' || request.fromCurrency === 'GRU') {
      const pair =
        request.fromCurrency === 'GRU'
          ? `${request.fromCurrency}/${request.toCurrency}`
          : `${request.fromCurrency}/${request.toCurrency}`;
      const rateData = await this.getRate(pair);
      rate = rateData.rate;
      toAmount = fromAmount.mul(new Decimal(rate));
    } else {
      // Convert through GRU as intermediary
      // fromCurrency -> GRU -> toCurrency
      const fromGruPair = `${request.fromCurrency}/GRU`;
      const toGruPair = `GRU/${request.toCurrency}`;

      const fromGruRate = await this.getRate(fromGruPair);
      const toGruRate = await this.getRate(toGruPair);

      // Convert: fromCurrency -> GRU -> toCurrency
      const gruAmount = fromAmount.div(new Decimal(fromGruRate.rate));
      toAmount = gruAmount.mul(new Decimal(toGruRate.rate));
      rate = new Decimal(toAmount).div(fromAmount).toString();
    }

    // Determine settlement asset
    const settlementAsset = request.settlementAsset || SettlementAsset.FIAT;

    // Store conversion record (optional)
    await prisma.nostroVostroTransfer.create({
      data: {
        transferId: conversionId,
        fromAccountId: 'CONVERSION', // Placeholder
        toAccountId: 'CONVERSION', // Placeholder
        fromParticipantId: 'SYSTEM',
        toParticipantId: 'SYSTEM',
        amount: toAmount,
        currency: request.toCurrency,
        settlementAsset,
        valueDate: request.valueDate ? new Date(request.valueDate) : new Date(),
        fxDetails: {
          fromCurrency: request.fromCurrency,
          toCurrency: request.toCurrency,
          fromAmount: request.amount,
          toAmount: toAmount.toString(),
          rate,
        },
        status: 'SETTLED',
        settledAt: new Date(),
        metadata: {
          conversionId,
          conversionType: 'GRU_FX',
        },
      },
    });

    return {
      conversionId,
      fromCurrency: request.fromCurrency,
      toCurrency: request.toCurrency,
      fromAmount: request.amount,
      toAmount: toAmount.toString(),
      rate,
      settlementAsset,
      timestamp: new Date(),
    };
  }

  /**
   * Get available GRU/FX pairs
   */
  async getAvailablePairs(): Promise<string[]> {
    // Get all active FX pairs from database
    const fxPairs = await prisma.fxPair.findMany({
      where: { status: 'active' },
    });

    const pairs: string[] = [];

    // Add standard FX pairs
    for (const pair of fxPairs) {
      pairs.push(`${pair.baseCurrency}/${pair.quoteCurrency}`);
    }

    // Add GRU pairs for major currencies
    const majorCurrencies = ['USD', 'EUR', 'GBP', 'JPY', 'CNY', 'CHF', 'AUD', 'CAD'];
    for (const currency of majorCurrencies) {
      pairs.push(`GRU/${currency}`);
      pairs.push(`${currency}/GRU`);
    }

    return pairs.sort();
  }

  /**
   * Parse currency pair string
   */
  private parsePair(pair: string): [string, string] {
    const parts = pair.split('/');
    if (parts.length !== 2) {
      throw new DbisError(ErrorCode.VALIDATION_ERROR, `Invalid pair format: ${pair}. Expected format: BASE/QUOTE`);
    }
    return [parts[0].toUpperCase(), parts[1].toUpperCase()];
  }

  /**
   * Get interval in milliseconds
   */
  private getIntervalMs(interval: '1h' | '1d' | '1w'): number {
    switch (interval) {
      case '1h':
        return 60 * 60 * 1000;
      case '1d':
        return 24 * 60 * 60 * 1000;
      case '1w':
        return 7 * 24 * 60 * 60 * 1000;
      default:
        return 24 * 60 * 60 * 1000;
    }
  }
}

export const gruFxService = new GruFxService();

