// live-prices.jsx — real price feed for the Portfolio Tracker.
// ─────────────────────────────────────────────────────────────────────────────
//  • Crypto  → CoinGecko (free, no API key, called straight from the browser).
//              Fetched in EUR *and* USD: USD is stored as the asset's native
//              price (so the existing cost-basis math stays correct) and the
//              EUR/USD pair is used to refresh the live FX rate, so every euro
//              figure in the app reflects the real European price.
//  • ETF     → Twelve Data, proxied through your Railway server so the API key
//              never reaches the browser. Set window.PT_PROXY_URL to enable it;
//              if it's empty, ETFs simply keep the built-in demo prices.
//
//  Prices are written back in each asset's NATIVE currency via provider.set(),
//  exactly like the mock provider, so nothing else in the app has to change.
//  Assets the feed doesn't cover (e.g. US stocks) keep ticking on the mock.
// ─────────────────────────────────────────────────────────────────────────────

window.LivePrices = (function () {
  // ticker (catalog key, before the dot) → CoinGecko coin id
  const CG_IDS = {
    BTC: "bitcoin", ETH: "ethereum", USDT: "tether", BNB: "binancecoin",
    SOL: "solana", XRP: "ripple", USDC: "usd-coin", ADA: "cardano",
    DOGE: "dogecoin", AVAX: "avalanche-2", TRX: "tron", LINK: "chainlink",
    DOT: "polkadot", MATIC: "matic-network", TON: "the-open-network",
    SHIB: "shiba-inu", LTC: "litecoin", BCH: "bitcoin-cash", UNI: "uniswap",
    ICP: "internet-computer", DAI: "dai", ETC: "ethereum-classic",
    XLM: "stellar", NEAR: "near", APT: "aptos", INJ: "injective-protocol",
    OP: "optimism", ARB: "arbitrum", FIL: "filecoin", IMX: "immutable-x",
    HBAR: "hedera-hashgraph", VET: "vechain", MKR: "maker", RNDR: "render-token",
    GRT: "the-graph", AAVE: "aave", ALGO: "algorand", QNT: "quant-network",
    FTM: "fantom", SAND: "the-sandbox", MANA: "decentraland",
    AXS: "axie-infinity", THETA: "theta-token", EGLD: "elrond-erd-2",
    XTZ: "tezos", CHZ: "chiliz", EOS: "eos", MINA: "mina-protocol",
    GALA: "gala", ZEC: "zcash", CRV: "curve-dao-token", LDO: "lido-dao",
    SUI: "sui", SEI: "sei-network", PEPE: "pepe", WIF: "dogwifcoin",
    BONK: "bonk", JUP: "jupiter-exchange-solana", TIA: "celestia",
    STX: "blockstack", RUNE: "thorchain", FET: "fetch-ai", ENA: "ethena",
  };

  const CG_URL = "https://api.coingecko.com/api/v3/simple/price";
  const CRYPTO_EVERY = 60 * 1000;   // CoinGecko: ~30 calls/min free → 1/min is safe
  const ETF_EVERY    = 90 * 1000;   // Twelve Data: 800 calls/day free
  const base = (sym) => sym.split(/[._]/)[0];

  let timers = [];
  const clearAll = () => { timers.forEach(clearInterval); timers = []; };

  // ── Crypto via CoinGecko ──────────────────────────────────────────────────
  async function pollCrypto(ctx) {
    const ids = [...new Set(
      ctx.symbols.filter((s) => CG_IDS[base(s)]).map((s) => CG_IDS[base(s)])
    )];
    if (!ids.length) return;
    try {
      const url = `${CG_URL}?ids=${ids.join(",")}&vs_currencies=eur,usd`;
      const res = await fetch(url, { headers: { accept: "application/json" } });
      if (!res.ok) throw new Error("CoinGecko " + res.status);
      const data = await res.json();

      // Live EUR/USD from BTC (or any coin that returned both) → real European pricing.
      for (const id of ids) {
        const row = data[id];
        if (row && row.eur && row.usd) { ctx.setFxUsd(row.eur / row.usd); break; }
      }

      const out = {};
      for (const s of ctx.symbols) {
        const id = CG_IDS[base(s)];
        const row = id && data[id];
        if (row && typeof row.usd === "number") out[s] = row.usd; // native (USD)
      }
      if (Object.keys(out).length) ctx.onPrices(out, "crypto");
      ctx.setStatus("crypto", "live");
    } catch (e) {
      ctx.setStatus("crypto", "error");
      console.info("[LivePrices] crypto offline — using demo prices");
    }
  }

  // ── ETF via Twelve Data (through the Railway proxy) ───────────────────────
  async function pollETF(ctx) {
    const proxy = (window.PT_PROXY_URL || "").replace(/\/$/, "");
    const syms = ctx.symbols.filter((s) => ctx.typeOf(s) === "etf" && ctx.currencyOf(s) === "EUR");
    if (!proxy || !syms.length) { ctx.setStatus("etf", proxy ? "idle" : "off"); return; }
    try {
      const url = `${proxy}/etf?symbols=${encodeURIComponent(syms.join(","))}`;
      const res = await fetch(url, { headers: { accept: "application/json" } });
      if (!res.ok) throw new Error("proxy " + res.status);
      const data = await res.json(); // { "SWDA.MI": 121.3, ... }  (EUR, native)
      const out = {};
      for (const [s, p] of Object.entries(data)) {
        const v = Number(p);
        if (isFinite(v) && v > 0) out[s] = v;
      }
      if (Object.keys(out).length) ctx.onPrices(out, "etf");
      ctx.setStatus("etf", "live");
    } catch (e) {
      ctx.setStatus("etf", "error");
      console.info("[LivePrices] etf offline — using demo prices");
    }
  }

  // ctx = { symbols, typeOf, currencyOf, onPrices, setFxUsd, setStatus }
  function start(ctx) {
    clearAll();
    pollCrypto(ctx); pollETF(ctx);                       // immediate first fetch
    timers.push(setInterval(() => pollCrypto(ctx), CRYPTO_EVERY));
    timers.push(setInterval(() => pollETF(ctx), ETF_EVERY));
    return clearAll;                                      // cleanup for useEffect
  }

  return { start, stop: clearAll, CG_IDS };
})();
