/**
 * Czech National Bank (ČNB) exchange rate service.
 * Fetches daily rates and caches them.
 * API: https://api.cnb.cz/cnbapi/exrates/daily
 */

interface CnbRate {
  currencyCode: string;
  rate: number;
  amount: number;
}

// Per-key cache: each key carries its OWN fetch timestamp so a hit on one date
// never refreshes the TTL of another. A stale entry is only re-fetched (and
// only that key is checked for the stale fallback).
const rateCache = new Map<
  string,
  { rates: Record<string, number>; time: number }
>();
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
const FETCH_TIMEOUT_MS = 8000; // abort a hung ČNB endpoint
const inflight = new Map<string, Promise<Record<string, number>>>();

async function fetchRatesForDate(
  date?: string,
): Promise<Record<string, number>> {
  const key = date || "today";
  const cached = rateCache.get(key);
  if (cached && Date.now() - cached.time <= CACHE_TTL_MS) return cached.rates;
  if (inflight.has(key)) return inflight.get(key)!;

  const promise = (async () => {
    try {
      let url = "https://api.cnb.cz/cnbapi/exrates/daily?lang=EN";
      if (date) url += `&date=${date}`;

      const response = await fetch(url, {
        signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
      });
      if (!response.ok) throw new Error(`CNB API: ${response.status}`);

      const data = (await response.json()) as { rates?: CnbRate[] };
      if (!Array.isArray(data.rates)) {
        throw new Error("CNB API: neočekávaný formát odpovědi (rates)");
      }
      const rates: Record<string, number> = { CZK: 1 };

      for (const r of data.rates) {
        rates[r.currencyCode] = r.rate / r.amount;
      }

      rateCache.set(key, { rates, time: Date.now() });
      return rates;
    } catch (err) {
      console.error("Failed to fetch CNB exchange rates:", err);
      // Stale-cache fallback for the SAME key (e.g. a hung/aborted refresh of
      // an entry that is past TTL — better to serve yesterday's rate than fail).
      const stale = rateCache.get(key);
      if (stale) return stale.rates;
      throw new Error("Nepodařilo se získat aktuální kurzy z ČNB");
    } finally {
      inflight.delete(key);
    }
  })();

  inflight.set(key, promise);
  return promise;
}

/**
 * Test-only: clear the module-level rate cache (and any in-flight fetches).
 * The cache persists for the process lifetime, so tests that assert on
 * specific fetched rates must reset it between runs to avoid order-dependent
 * results (a populated "today" entry would otherwise short-circuit the mock).
 * Not used by runtime code.
 */
export function __resetRateCacheForTest(): void {
  rateCache.clear();
  inflight.clear();
}

/** Convert an amount from a given currency to CZK using CNB rates */
export async function toCzk(
  amount: number,
  currency: string,
  date?: string,
): Promise<number> {
  if (currency === "CZK") return amount;
  const rates = await fetchRatesForDate(date);
  const rate = rates[currency];
  if (!rate) throw new Error(`Neznámá měna: ${currency}`);
  return Math.round(amount * rate * 100) / 100;
}

/** Get CNB rate for a currency (CZK per 1 unit), optionally for a specific date */
export async function getRate(
  currency: string,
  date?: string,
): Promise<number> {
  if (currency === "CZK") return 1;
  const rates = await fetchRatesForDate(date);
  const rate = rates[currency];
  if (!rate) throw new Error(`Neznámá měna: ${currency}`);
  return rate;
}
