/**
 * Crypto.com OTC Price Provider
 * Provides market prices from OTC quotes for FX service integration
 * Caches prices from received quotes for use in getMarketPrice()
 */

import { CryptoComRestClient } from '../clients/rest-client';
import { Quote, OtcInstrument } from '../types';
import { RESPONSE_CODES } from '../config';

const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes

interface CachedPrice {
  price: string;
  bid: string;
  ask: string;
  timestamp: number;
}

/**
 * Maps FX pair format (BTC/USD) to OTC instrument format (BTC_USD)
 */
function pairToInstrument(pair: string): string {
  return pair.replace('/', '_');
}

/**
 * Maps OTC instrument (BTC_USD) to FX pair format (BTC/USD)
 */
function instrumentToPair(instrument: string): string {
  return instrument.replace('_', '/');
}

export interface OtcPriceProviderConfig {
  restClient: CryptoComRestClient;
  cacheTtlMs?: number;
  minAmountForOtc?: number; // Minimum amount to use OTC prices (default: no minimum)
}

/**
 * Price provider that caches OTC quote prices for FX service integration
 */
export class OtcPriceProvider {
  private restClient: CryptoComRestClient;
  private cache: Map<string, CachedPrice> = new Map();
  private instruments: Map<string, OtcInstrument> = new Map();
  private cacheTtlMs: number;
  private minAmountForOtc: number;

  constructor(config: OtcPriceProviderConfig) {
    this.restClient = config.restClient;
    this.cacheTtlMs = config.cacheTtlMs ?? CACHE_TTL_MS;
    this.minAmountForOtc = config.minAmountForOtc ?? 0;
  }

  /**
   * Update price cache from a received quote
   * Call this when the OTC service receives a new quote
   */
  updateFromQuote(quote: Quote): void {
    for (const leg of quote.leg_list) {
      if (leg.bid && leg.ask && leg.instrument_name) {
        const pair = instrumentToPair(leg.instrument_name);
        const midPrice = ((parseFloat(leg.bid) + parseFloat(leg.ask)) / 2).toFixed(8);
        this.cache.set(pair, {
          price: midPrice,
          bid: leg.bid,
          ask: leg.ask,
          timestamp: Date.now(),
        });
      }
    }
  }

  /**
   * Get price for FX pair - returns cached price if available and fresh
   */
  getPrice(pair: string, _amount?: number): string | null {
    const cached = this.cache.get(pair);
    if (!cached) return null;

    const age = Date.now() - cached.timestamp;
    if (age > this.cacheTtlMs) {
      this.cache.delete(pair);
      return null;
    }

    return cached.price;
  }

  /**
   * Get bid/ask for pair
   */
  getBidAsk(pair: string): { bid: string; ask: string } | null {
    const cached = this.cache.get(pair);
    if (!cached) return null;

    const age = Date.now() - cached.timestamp;
    if (age > this.cacheTtlMs) {
      this.cache.delete(pair);
      return null;
    }

    return { bid: cached.bid, ask: cached.ask };
  }

  /**
   * Fetch and cache price from open quotes (fallback when no cached price)
   * Rate limited - use sparingly
   */
  async refreshPrice(pair: string): Promise<string | null> {
    const instrument = pairToInstrument(pair);
    const response = await this.restClient.getOpenQuotes({
      limit: 10,
    });

    if (response.code !== RESPONSE_CODES.SUCCESS || !response.result?.quote_list) {
      return null;
    }

    for (const quote of response.result.quote_list) {
      for (const leg of quote.leg_list) {
        if (leg.instrument_name === instrument && leg.bid && leg.ask) {
          const midPrice = ((parseFloat(leg.bid) + parseFloat(leg.ask)) / 2).toFixed(8);
          this.cache.set(pair, {
            price: midPrice,
            bid: leg.bid,
            ask: leg.ask,
            timestamp: Date.now(),
          });
          return midPrice;
        }
      }
    }

    return null;
  }

  /**
   * Load instruments and build pair mapping
   */
  async loadInstruments(): Promise<void> {
    const response = await this.restClient.getOtcInstruments();
    if (response.code !== RESPONSE_CODES.SUCCESS || !response.result?.instrument_list) {
      return;
    }

    this.instruments.clear();
    for (const inst of response.result.instrument_list) {
      this.instruments.set(inst.instrument_name, inst);
    }
  }

  /**
   * Check if pair is supported by OTC
   */
  isPairSupported(pair: string): boolean {
    const instrument = pairToInstrument(pair);
    return this.instruments.has(instrument);
  }

  /**
   * Clear cache (e.g. on disconnect)
   */
  clearCache(): void {
    this.cache.clear();
  }

  /**
   * Get cache stats
   */
  getCacheStats(): { size: number; pairs: string[] } {
    return {
      size: this.cache.size,
      pairs: Array.from(this.cache.keys()),
    };
  }
}
