/**
 * Exchange rate feed — 5-min cached CoinGecko USD/fiat pairs.
 *
 * cUSDC is pegged to USDC (≈ $1 USD), so cUSDC/fiat ≈ USD/fiat.
 * $MYCO uses bonding curve price × USD/fiat rate.
 */

import type { TokenId, FiatCurrency } from './schemas';

// ── Cache ──

interface RateEntry {
	rates: Record<string, number>; // fiat currency → USD/fiat rate
	ts: number;
}

const TTL = 5 * 60 * 1000;
let cached: RateEntry | null = null;
let inFlight: Promise<RateEntry> | null = null;

const CG_API_KEY = process.env.COINGECKO_API_KEY || '';

function cgUrl(url: string): string {
	if (!CG_API_KEY) return url;
	const sep = url.includes('?') ? '&' : '?';
	return `${url}${sep}x_cg_demo_api_key=${CG_API_KEY}`;
}

const FIAT_IDS: Record<FiatCurrency, string> = {
	EUR: 'eur', USD: 'usd', GBP: 'gbp', BRL: 'brl',
	MXN: 'mxn', INR: 'inr', NGN: 'ngn', ARS: 'ars',
};

async function fetchRates(): Promise<RateEntry> {
	if (cached && Date.now() - cached.ts < TTL) return cached;
	if (inFlight) return inFlight;

	inFlight = (async (): Promise<RateEntry> => {
		try {
			const currencies = Object.values(FIAT_IDS).join(',');
			const res = await fetch(
				cgUrl(`https://api.coingecko.com/api/v3/simple/price?ids=usd-coin&vs_currencies=${currencies}`),
				{ headers: { accept: 'application/json' }, signal: AbortSignal.timeout(10000) },
			);
			if (res.status === 429) {
				console.warn('[exchange-rates] CoinGecko rate limited, waiting 60s...');
				await new Promise(r => setTimeout(r, 60000));
				const retry = await fetch(
					cgUrl(`https://api.coingecko.com/api/v3/simple/price?ids=usd-coin&vs_currencies=${currencies}`),
					{ headers: { accept: 'application/json' }, signal: AbortSignal.timeout(10000) },
				);
				if (!retry.ok) return cached || { rates: {}, ts: Date.now() };
				const data = await retry.json() as any;
				const entry: RateEntry = { rates: data['usd-coin'] || {}, ts: Date.now() };
				cached = entry;
				return entry;
			}
			if (!res.ok) return cached || { rates: {}, ts: Date.now() };
			const data = await res.json() as any;
			const entry: RateEntry = { rates: data['usd-coin'] || {}, ts: Date.now() };
			cached = entry;
			return entry;
		} catch (e) {
			console.warn('[exchange-rates] Failed to fetch rates:', e);
			return cached || { rates: {}, ts: Date.now() };
		} finally {
			inFlight = null;
		}
	})();

	return inFlight;
}

/**
 * Get the exchange rate for a token in a fiat currency.
 * Returns fiat amount per 1 whole token (not base units).
 */
export async function getExchangeRate(tokenId: TokenId, fiat: FiatCurrency): Promise<number> {
	const fiatKey = FIAT_IDS[fiat];
	if (!fiatKey) return 0;

	const entry = await fetchRates();
	const usdFiatRate = entry.rates[fiatKey] || 0;

	if (tokenId === 'cusdc' || tokenId === 'fusdc') {
		// cUSDC/fUSDC ≈ 1 USD, so rate = USD/fiat
		return usdFiatRate;
	}

	if (tokenId === 'myco') {
		// $MYCO price from bonding curve × USD/fiat
		try {
			const { calculatePrice } = await import('../../server/bonding-curve');
			const { getTokenDoc } = await import('../../server/token-service');
			const doc = getTokenDoc('myco');
			const supply = doc?.token?.totalSupply || 0;
			const priceInCusdcBase = calculatePrice(supply);
			// priceInCusdcBase is cUSDC base units per 1 MYCO base unit
			// Convert to USD: base / 1_000_000
			const priceInUsd = priceInCusdcBase / 1_000_000;
			return priceInUsd * usdFiatRate;
		} catch {
			return 0;
		}
	}

	return 0;
}

/**
 * Get all supported fiat rates for a token. Returns { currency → rate }.
 */
export async function getAllRates(tokenId: TokenId): Promise<Record<string, number>> {
	const result: Record<string, number> = {};
	const fiats: FiatCurrency[] = ['EUR', 'USD', 'GBP', 'BRL', 'MXN', 'INR', 'NGN', 'ARS'];
	// Fetch once (cached), then compute per-fiat
	await fetchRates();
	for (const fiat of fiats) {
		result[fiat] = await getExchangeRate(tokenId, fiat);
	}
	return result;
}
