// Live pricing for the compare page.
//
// Prices in compare-data.jsx are hand-maintained and drift; this module pulls
// each product's *current* price straight from the manufacturer at runtime.
//
// IMPORTANT — no FX conversion. An earlier version converted a base price with
// exchange rates and it was wrong: manufacturers set their OWN per-country
// prices (Pando charges £288 / $379 / €329, none of which is an FX conversion
// of the others), and Shopify silently localises prices to the visitor's
// country. So we ask each store for its own price *in each currency* and show
// exactly that — never a number we computed ourselves.
//
// Sources:
//   • Shopify (Pando Moto, Bowtex, MotoGirl) — GET /products/<handle>.js?currency=X
//     for X in GBP/USD/EUR (served with Access-Control-Allow-Origin: *, price in
//     minor units). A store that doesn't offer currency X returns its base-
//     currency amount unchanged, so we detect "not offered" by comparing X to
//     the store's base-currency amount and fall back to showing the real price
//     in the currency the store actually sells in.
//   • WooCommerce (Knox) — no cross-origin reads + slugs that 301-redirect, so
//     it goes through our Netlify function /.netlify/functions/knox-price
//     (GBP only; USD/EUR are "not offered" -> shown in GBP).
//
// A failed fetch never falls back to the hardcoded compare-data figure — it
// surfaces as a "check price on site" link (HeroCell) so we never show a
// stale/wrong number. Results cache in sessionStorage per session.
//
// Everything is in an IIFE; only window.useLivePrices and window.mmFormatPrice
// leak out (shared global scope — see CLAUDE.md). Internals are _lp*-prefixed.

(function () {
  const _LP_SYMBOL = { GBP: "£", USD: "$", EUR: "€" };
  // Base selling currency per brand — the currency a store falls back to when
  // it doesn't offer the one we asked for.
  const _LP_BASE = { pandomoto: "EUR", bowtex: "EUR", motogirl: "GBP", knox: "GBP" };
  const _LP_CURS = ["GBP", "USD", "EUR"];
  const _LP_TTL = 30 * 60 * 1000;     // 30-minute freshness window
  const _LP_SS = "mm_live_prices_v2"; // sessionStorage bucket (id -> {resolved, ts})

  // ── sessionStorage cache ────────────────────────────────────────────────
  const _lpReadSS = () => {
    try { return JSON.parse(sessionStorage.getItem(_LP_SS)) || {}; }
    catch (e) { return {}; }
  };
  const _lpWriteSS = (patch) => {
    try { sessionStorage.setItem(_LP_SS, JSON.stringify(Object.assign(_lpReadSS(), patch))); }
    catch (e) { /* storage full/disabled — skip the cache */ }
  };
  const _lpFresh = (rec) => rec && (Date.now() - rec.ts) < _LP_TTL;

  // ── helpers ─────────────────────────────────────────────────────────────
  const _lpSlug = (url) => url.replace(/[?#].*$/, "").replace(/\/+$/, "").split("/").pop();
  // Shopify handle: normally the last path segment of shopUrl, but a product's
  // URL slug can differ from its real handle — allow a `liveHandle` override.
  const _lpShopHandle = (product) => product.liveHandle || _lpSlug(product.shopUrl);
  const _lpHost = (url) => (url.match(/^https?:\/\/([^/]+)/) || [])[1];
  // .js gives price_min/price in minor units (lowest variant — sizes are almost
  // always equal, and min never overstates the cost).
  const _lpMinor = (d) => (d && d.price_min != null ? d.price_min : d && d.price);
  const _lpNum = (n) => {
    const r = Math.round(n * 100) / 100;
    return Number.isInteger(r) ? String(r) : r.toFixed(2);
  };

  // A "resolved" map is { GBP|USD|EUR: { amount, currency, offered } } where
  // `currency` is the currency the `amount` is actually in (== the key when the
  // store offers it, otherwise the store's base currency).
  async function _lpFetchShopify(product) {
    const host = _lpHost(product.shopUrl);
    const handle = _lpShopHandle(product);
    const base = _LP_BASE[product.brandId] || "GBP";

    const amounts = {};
    await Promise.all(_LP_CURS.map(async (c) => {
      const r = await fetch(`https://${host}/products/${handle}.js?currency=${c}`,
        { headers: { Accept: "application/json" } });
      if (!r.ok) throw new Error("shopify " + c + " " + r.status);
      const minor = _lpMinor(await r.json());
      if (!isFinite(minor)) throw new Error("shopify " + c + " no price");
      amounts[c] = minor;
    }));

    const baseAmt = amounts[base];
    const resolved = {};
    _LP_CURS.forEach((c) => {
      const offered = c === base || amounts[c] !== baseAmt;
      resolved[c] = offered
        ? { amount: amounts[c] / 100, currency: c, offered: true }
        : { amount: baseAmt / 100, currency: base, offered: false };
    });
    return resolved;
  }

  async function _lpFetchKnox(product) {
    const r = await fetch(
      `/.netlify/functions/knox-price?slug=${encodeURIComponent(_lpSlug(product.shopUrl))}`);
    if (!r.ok) throw new Error("knox " + r.status);
    const d = await r.json();
    if (!isFinite(d.amount)) throw new Error("knox no amount");
    const cur = d.currency || "GBP";
    const mk = (c) => ({ amount: Number(d.amount), currency: cur, offered: c === cur });
    return { GBP: mk("GBP"), USD: mk("USD"), EUR: mk("EUR") };
  }

  const _lpFetcher = (product) =>
    product.brandId === "knox" ? _lpFetchKnox : _lpFetchShopify;

  // ── hook ────────────────────────────────────────────────────────────────
  // useLivePrices(ids) -> { [id]: { status:'loading'|'ok'|'error', resolved } }
  function useLivePrices(ids) {
    const [state, setState] = React.useState({});
    const started = React.useRef({});           // id -> true (fetched this mount)
    const key = ids.filter(Boolean).join(",");

    React.useEffect(() => {
      ids.filter(Boolean).forEach((id) => {
        if (started.current[id]) return;
        started.current[id] = true;

        const product = window.COMPARE_DATA.byId(id);
        if (!product) return;

        const cached = _lpReadSS()[id];
        if (_lpFresh(cached)) {
          setState((p) => ({ ...p, [id]: { status: "ok", resolved: cached.resolved } }));
          return;
        }
        setState((p) => ({ ...p, [id]: { status: "loading" } }));
        _lpFetcher(product)(product)
          .then((resolved) => {
            setState((p) => ({ ...p, [id]: { status: "ok", resolved } }));
            _lpWriteSS({ [id]: { resolved, ts: Date.now() } });
          })
          .catch(() => setState((p) => ({ ...p, [id]: { status: "error" } })));
      });
    }, [key]);

    return state;
  }

  // entry -> "£288" / "€329" (symbol from the amount's real currency).
  const _lpFormat = (entry) =>
    entry && isFinite(entry.amount) ? _LP_SYMBOL[entry.currency] + _lpNum(entry.amount) : null;

  window.useLivePrices = useLivePrices;
  window.mmFormatPrice = _lpFormat;
})();
