import axios from 'axios';
import { logger } from '../utils/logger';
import { primeLiveReferencePriceUsd } from './canonical-price-oracle';

const FRANKFURTER_CURRENCIES = ['EUR', 'GBP', 'AUD', 'CAD', 'CHF', 'JPY'] as const;
const COINGECKO_REFERENCE_IDS: Record<string, string> = {
  ETH: 'ethereum',
  BTC: 'bitcoin',
  LINK: 'chainlink',
  XAU: 'pax-gold',
};

const COINGECKO_COMMODITY_IDS = {
  platinum: 'platinum',
  palladium: 'palladium',
  copper: 'copper',
  nickel: 'nickel',
  cobalt: 'cobalt',
} as const;

let lastRefreshAt = 0;
let refreshInFlight: Promise<void> | null = null;
const DEFAULT_TTL_MS = 5 * 60 * 1000;

function coingeckoBaseUrl(): string {
  return process.env.COINGECKO_API_KEY
    ? 'https://pro-api.coingecko.com/api/v3'
    : 'https://api.coingecko.com/api/v3';
}

function coingeckoHeaders(): Record<string, string> {
  const key = process.env.COINGECKO_API_KEY?.trim();
  return key ? { 'x-cg-pro-api-key': key } : {};
}

async function fetchFrankfurterUsdPerUnit(currency: string): Promise<number | undefined> {
  try {
    const response = await axios.get<{ rates?: { USD?: number } }>(
      `https://api.frankfurter.app/latest?from=${encodeURIComponent(currency)}&to=USD`,
      { timeout: 8000 },
    );
    const usd = response.data?.rates?.USD;
    if (usd !== undefined && Number.isFinite(usd) && usd > 0) {
      return usd;
    }
  } catch (error) {
    logger.warn(`Frankfurter FX fetch failed for ${currency}:`, error);
  }
  return undefined;
}

async function fetchCoingeckoUsd(coinId: string): Promise<number | undefined> {
  try {
    const response = await axios.get<Record<string, { usd?: number }>>(
      `${coingeckoBaseUrl()}/simple/price`,
      {
        timeout: 8000,
        headers: coingeckoHeaders(),
        params: {
          ids: coinId,
          vs_currencies: 'usd',
        },
      },
    );
    const usd = response.data?.[coinId]?.usd;
    if (usd !== undefined && Number.isFinite(usd) && usd > 0) {
      return usd;
    }
  } catch (error) {
    logger.warn(`CoinGecko FX fetch failed for ${coinId}:`, error);
  }
  return undefined;
}

/** Refresh live ISO-4217 USD reference rates used for c* / cW* valuation. */
export async function refreshIso4217FxRates(ttlMs = DEFAULT_TTL_MS): Promise<void> {
  primeLiveReferencePriceUsd('USD', 1, ttlMs);

  const commodityEntries = await Promise.all(
    Object.entries(COINGECKO_COMMODITY_IDS).map(async ([key, coinId]) => {
      const priceUsd = await fetchCoingeckoUsd(coinId);
      return [key, priceUsd] as const;
    }),
  );
  const commodities = Object.fromEntries(commodityEntries.filter(([, price]) => price != null)) as Partial<
    Record<keyof typeof COINGECKO_COMMODITY_IDS, number>
  >;

  await Promise.all([
    ...FRANKFURTER_CURRENCIES.map(async (currency) => {
      const priceUsd = await fetchFrankfurterUsdPerUnit(currency);
      if (priceUsd !== undefined) {
        primeLiveReferencePriceUsd(currency, priceUsd, ttlMs);
      }
    }),
    ...Object.entries(COINGECKO_REFERENCE_IDS).map(async ([symbol, coinId]) => {
      const priceUsd = await fetchCoingeckoUsd(coinId);
      if (priceUsd !== undefined) {
        primeLiveReferencePriceUsd(symbol, priceUsd, ttlMs);
      }
    }),
  ]);

  if (commodities.platinum != null && commodities.palladium != null) {
    primeLiveReferencePriceUsd('LIPMG', (commodities.platinum + commodities.palladium) / 2, ttlMs);
  }
  if (commodities.copper != null) {
    primeLiveReferencePriceUsd('LIBMG1', commodities.copper, ttlMs);
  }
  if (commodities.nickel != null && commodities.cobalt != null) {
    primeLiveReferencePriceUsd('LIBMG2', commodities.nickel * 0.8 + commodities.cobalt * 0.2, ttlMs);
  } else if (commodities.nickel != null) {
    primeLiveReferencePriceUsd('LIBMG2', commodities.nickel, ttlMs);
  }

  lastRefreshAt = Date.now();
}

export async function ensureIso4217FxPrimed(ttlMs = DEFAULT_TTL_MS): Promise<void> {
  if (Date.now() - lastRefreshAt < ttlMs) {
    return;
  }
  if (!refreshInFlight) {
    refreshInFlight = refreshIso4217FxRates(ttlMs).finally(() => {
      refreshInFlight = null;
    });
  }
  await refreshInFlight;
}

export function startIso4217FxScheduler(intervalMs = DEFAULT_TTL_MS): NodeJS.Timeout {
  void refreshIso4217FxRates(intervalMs);
  return setInterval(() => {
    void refreshIso4217FxRates(intervalMs);
  }, intervalMs);
}

export function getIso4217FxLastRefreshAt(): number {
  return lastRefreshAt;
}
