// The money bar: chips, wallet, buying in, cashing out.
//
// Buying chips is not instant and cannot be. gogobee owns the euros and has no
// inbound API, so it polls Pete for the crossing, moves the money on its side,
// and pushes the verdict back — a few seconds, end to end. So the button does
// not lie about it: the chips show as "buying" until they are real, and this
// file polls until they are. Nothing spendable appears until gogobee has said
// it took the euros.
//
// Exposed as window.PeteGames so the table (blackjack.js) shares one view of
// the money rather than keeping a second copy that drifts from this one.
(function () {
  "use strict";

  var bar = document.querySelector("[data-chipbar]");
  if (!bar) return;

  var chipsEl   = bar.querySelector("[data-chips]");
  var pendingEl = bar.querySelector("[data-pending]");
  var eurosEl   = bar.querySelector("[data-euros]");
  var amountEl  = bar.querySelector("[data-buyin-amount]");
  var buyBtn    = bar.querySelector("[data-buyin]");
  var cashBtn   = bar.querySelector("[data-cashout]");
  var msgEl     = bar.querySelector("[data-chipbar-msg]");

  var listeners = [];
  var view = null;
  var pollTimer = null;
  var pollUntil = 0;

  function money(n) {
    return (n || 0).toLocaleString();
  }

  function say(text, tone) {
    if (!msgEl) return;
    if (!text) { msgEl.classList.add("hidden"); return; }
    msgEl.textContent = text;
    msgEl.classList.remove("hidden");
    msgEl.style.color = tone === "bad" ? "#cc3d4a" : "";
  }

  function paint(v) {
    var first = view === null;
    view = v;
    // The chip count rolls to its new value rather than jumping to it — the table
    // times this to land with the chips that caused it, so a payout reads as the
    // number catching up with the felt. On the first paint there's nothing to
    // catch up with, so it just sets.
    if (chipsEl) {
      if (first || !window.PeteFX) chipsEl.textContent = money(v.chips);
      else window.PeteFX.count(chipsEl, v.chips);
    }
    if (eurosEl) eurosEl.textContent = (v.euros || 0).toFixed(2);

    if (pendingEl) {
      if (v.pending > 0) {
        pendingEl.textContent = "+" + money(v.pending) + " buying…";
        pendingEl.classList.remove("hidden");
      } else {
        pendingEl.classList.add("hidden");
      }
    }

    // You cannot cash out mid-game: the stake is already on the table. `game` is
    // set by whichever game you're in, so this holds for any of them — checking
    // for a blackjack hand specifically would let you walk out on a hangman.
    if (cashBtn) cashBtn.disabled = v.chips <= 0 || !!v.game;
    if (buyBtn) buyBtn.disabled = v.chips + v.pending >= v.cap;

    listeners.forEach(function (fn) { fn(v); });
  }

  // pollPending keeps asking while a buy-in is in flight, and stops the moment
  // it lands — or is refused, which looks the same from here (pending drops to
  // zero) and is told apart by whether the chips arrived.
  function pollPending() {
    clearTimeout(pollTimer);
    if (Date.now() > pollUntil) {
      say("gogobee hasn't answered yet. Your euros are safe — give it a moment and reload.");
      return;
    }
    pollTimer = setTimeout(function () {
      get().then(function (v) {
        if (!v) return;
        if (v.pending > 0) { pollPending(); return; }
        if (v.chips > (pollPending.was || 0)) {
          say("Chips are yours. Good luck!");
        } else {
          say("gogobee wouldn't cover that — not enough euros in your wallet.", "bad");
        }
      });
    }, 1200);
  }

  function request(path, body) {
    return fetch(path, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body || {}),
    }).then(function (res) {
      return res.json().catch(function () { return {}; }).then(function (data) {
        if (!res.ok) throw new Error(data.error || "that didn't work");
        return data;
      });
    });
  }

  function get() {
    return fetch("/api/games/table", { headers: { "Accept": "application/json" } })
      .then(function (res) { return res.ok ? res.json() : null; })
      .then(function (v) { if (v) paint(v); return v; })
      .catch(function () { return null; });
  }

  if (buyBtn) {
    buyBtn.addEventListener("click", function () {
      var amount = parseInt(amountEl && amountEl.value, 10);
      if (!(amount > 0)) { say("How many chips?", "bad"); return; }

      buyBtn.disabled = true;
      say("Asking gogobee for " + money(amount) + " euros…");
      pollPending.was = view ? view.chips : 0;

      request("/api/games/buyin", { amount: amount })
        .then(function (v) {
          paint(v);
          pollUntil = Date.now() + 60000;
          pollPending();
        })
        .catch(function (err) {
          say(err.message, "bad");
          buyBtn.disabled = false;
        });
    });
  }

  if (cashBtn) {
    cashBtn.addEventListener("click", function () {
      cashBtn.disabled = true;
      say("Cashing you out…");
      request("/api/games/cashout", { amount: 0 })
        .then(function (v) {
          paint(v);
          say("Chips are on their way back to euros. They'll show in your wallet shortly.");
          // The euro balance Pete shows is whatever gogobee last told it, so it
          // only moves once the credit has actually gone through over there.
          setTimeout(get, 4000);
        })
        .catch(function (err) {
          say(err.message, "bad");
          cashBtn.disabled = false;
        });
    });
  }

  // The room's volume. The button lives in the chip bar, which is the one thing
  // every table has — the sound belongs to the room, not to any one game — and
  // the state itself belongs to PeteSFX, which persists it. This only draws it.
  var sfxBtn = document.querySelector("[data-sfx-toggle]");
  if (sfxBtn && window.PeteSFX) {
    var onIcon  = sfxBtn.querySelector("[data-sfx-on]");
    var offIcon = sfxBtn.querySelector("[data-sfx-off]");
    var sfxLbl  = sfxBtn.querySelector("[data-sfx-label]");

    window.PeteSFX.onChange(function (muted) {
      sfxBtn.setAttribute("aria-pressed", muted ? "true" : "false");
      sfxBtn.title = muted ? "Sound is off" : "Sound is on";
      if (onIcon) onIcon.classList.toggle("hidden", muted);
      if (offIcon) offIcon.classList.toggle("hidden", !muted);
      if (sfxLbl) sfxLbl.textContent = muted ? "Turn the sound on" : "Mute the room";
    });
    sfxBtn.addEventListener("click", function () { window.PeteSFX.toggle(); });
  }

  window.PeteGames = {
    // onUpdate registers a listener called on every fresh view of the money.
    onUpdate: function (fn) { listeners.push(fn); if (view) fn(view); },
    // apply pushes a view the table already fetched (a deal answers with one),
    // so playing a hand doesn't need a second round-trip to refresh the chips.
    apply: paint,
    refresh: get,
    post: request,
    say: say,
    view: function () { return view; },
  };

  get();
})();
