// Real-time FX rates service with caching + fallback.
// Default fetch: exchangerate.host (no key, mid-market). Fallback: static seed data.
// Cached in-memory + sessionStorage with 10-minute TTL.

const CACHE_TTL_MS = 10 * 60 * 1000;
const CACHE_KEY = "atlas.rates.cache.v1";
const listeners = new Set();
const state = {
  status: "idle",        // idle | loading | ready | error | stale
  base: "USD",           // the base the rates are expressed against
  rates: null,           // { USD: 1, EUR: 0.91, ... }
  updatedAt: 0,
  source: "seed",        // 'live' | 'cache' | 'seed'
  error: null,
};

function seedRates() {
  // Rates relative to USD, derived from CURRENCIES[].rate (USD = 1)
  const out = {};
  window.CURRENCIES.forEach(c => { out[c.code] = c.rate; });
  return out;
}

function loadCache() {
  try {
    const raw = sessionStorage.getItem(CACHE_KEY);
    if (!raw) return null;
    const obj = JSON.parse(raw);
    if (!obj || !obj.rates || !obj.updatedAt) return null;
    return obj;
  } catch { return null; }
}
function saveCache(obj) {
  try { sessionStorage.setItem(CACHE_KEY, JSON.stringify(obj)); } catch {}
}

function notify() { listeners.forEach(fn => fn(state)); }

async function fetchLive() {
  // Try multiple providers; accept first success
  const providers = [
    { name: "exchangerate.host", url: "https://api.exchangerate.host/latest?base=USD", map: (d) => d.rates },
    { name: "open.er-api.com",   url: "https://open.er-api.com/v6/latest/USD",       map: (d) => d.rates },
    { name: "frankfurter.app",   url: "https://api.frankfurter.app/latest?from=USD", map: (d) => d.rates },
  ];
  for (const p of providers) {
    try {
      const res = await fetch(p.url, { cache: "no-store" });
      if (!res.ok) continue;
      const json = await res.json();
      const rates = p.map(json);
      if (rates && rates.EUR && rates.JPY) {
        return { rates: { USD: 1, ...rates }, provider: p.name };
      }
    } catch (e) { /* try next */ }
  }
  throw new Error("All providers failed");
}

async function refresh({ force = false } = {}) {
  // Use cache if fresh
  if (!force) {
    const cached = loadCache();
    if (cached && Date.now() - cached.updatedAt < CACHE_TTL_MS) {
      Object.assign(state, { status: "ready", rates: cached.rates, updatedAt: cached.updatedAt, source: "cache", error: null });
      notify();
      return;
    }
  }
  state.status = "loading"; notify();
  try {
    const { rates, provider } = await fetchLive();
    Object.assign(state, { status: "ready", rates, updatedAt: Date.now(), source: "live", error: null });
    saveCache({ rates, updatedAt: state.updatedAt, provider });
    notify();
  } catch (err) {
    // Fallback chain: cache (even stale) → seed
    const cached = loadCache();
    if (cached) {
      Object.assign(state, { status: "stale", rates: cached.rates, updatedAt: cached.updatedAt, source: "cache", error: err.message });
    } else {
      Object.assign(state, { status: "error", rates: seedRates(), updatedAt: Date.now(), source: "seed", error: err.message });
    }
    notify();
  }
}

function convert(baseCode, targetCode) {
  const r = state.rates || seedRates();
  const b = r[baseCode], t = r[targetCode];
  if (!b || !t) return 0;
  return (1 / b) * t;
}

function subscribe(fn) {
  listeners.add(fn);
  fn(state);
  return () => listeners.delete(fn);
}

function init() {
  // Seed immediately so UI has something
  state.rates = seedRates();
  state.updatedAt = Date.now();
  state.status = "idle";
  notify();
  refresh();
  // Auto-refresh every 10 minutes (polite polling)
  setInterval(() => refresh({ force: true }), CACHE_TTL_MS);
}

window.RatesService = { init, refresh, convert, subscribe, get state() { return state; } };
