/* global window, setTimeout */
/*
 * Boot state — the platform is up, the data is not.
 *
 * When somebody leaves the first run before the read has finished, they land
 * in a real workspace with nothing in it. The wrong answers to that are an
 * empty page (looks broken), a spinner over everything (looks stuck), or the
 * old numbers (looks fine and is a lie). What is left is the honest one: show
 * the shell, mark every surface that is still waiting, keep the one thing
 * that works working, and offer to fetch them when it is done.
 *
 * Everything here hangs off one signal — whether WinLoss has the closed book —
 * so a widget never has to decide for itself whether it is allowed to draw.
 */
(function () {
  "use strict";

  var CHANNELS = [
    { id: "slack", label: "Slack", detail: "a DM from Addy" },
    { id: "email", label: "Email", detail: "to your work address" },
    { id: "whatsapp", label: "WhatsApp", detail: "one message, no thread" },
    { id: "teams", label: "Teams", detail: "a chat from the Nudge app" },
  ];

  var state = {
    // "ready"    — the analysis is in, everything draws normally
    // "analysing" — the shell is usable, data-backed surfaces are waiting
    status: "ready",
    since: null,
    channel: null,       // where they asked to be told
    announced: false,    // have we told them it finished
  };
  var subs = [];
  function subscribe(fn) {
    subs.push(fn);
    return function () { subs = subs.filter(function (f) { return f !== fn; }); };
  }
  function notify() {
    subs.slice().forEach(function (fn) { try { fn(state); } catch (e) { /* a listener must not break boot */ } });
  }

  function dataReady() {
    try { return !!(window.WinLoss && window.WinLoss.ready && window.WinLoss.ready()); }
    catch (e) { return true; }
  }

  // Called when somebody enters the workspace ahead of the data. If the read
  // has already landed there is nothing to announce and nothing to wait for.
  function enter() {
    if (dataReady()) return false;
    state.status = "analysing";
    state.since = Date.now();
    state.announced = false;
    notify();
    watch();
    return true;
  }

  var watching = false;
  function watch() {
    if (watching) return;
    watching = true;
    var unsub = null;
    var finish = function () {
      if (state.status !== "analysing") return;
      state.status = "ready";
      notify();
      // Tell them the way they asked to be told. In the prototype the channel
      // send is a toast naming the channel rather than a real message — the
      // choice is the product decision being demonstrated, not the transport.
      if (!state.announced) {
        state.announced = true;
        var where = state.channel ? channelById(state.channel) : null;
        if (window.fireToast) {
          window.fireToast(where
            ? window.t("Your closed book is ready — we pinged you on {channel}.", { channel: where.label })
            : window.t("Your closed book is ready."));
        }
      }
      if (unsub) unsub();
      watching = false;
    };
    try {
      if (window.WinLoss && window.WinLoss.subscribe) {
        unsub = window.WinLoss.subscribe(function () { if (dataReady()) finish(); });
      }
    } catch (e) { /* fall through to the poll */ }
    // A poll as well as the subscription: the engine can degrade rather than
    // resolve, and a banner that never clears is worse than no banner.
    var poll = setInterval(function () {
      if (dataReady() || (window.WinLoss && window.WinLoss.state && window.WinLoss.state().historyStatus === "degraded")) {
        clearInterval(poll);
        finish();
      }
    }, 600);
  }

  function channelById(id) {
    for (var i = 0; i < CHANNELS.length; i++) if (CHANNELS[i].id === id) return CHANNELS[i];
    return null;
  }

  function setChannel(id) {
    state.channel = id;
    notify();
    var c = channelById(id);
    if (c && window.fireToast) {
      window.fireToast(window.t("We'll tell you on {channel} the moment it's ready.", { channel: c.label }));
    }
  }

  function waitedSeconds() {
    return state.since ? Math.floor((Date.now() - state.since) / 1000) : 0;
  }

  window.NudgeBoot = {
    CHANNELS: CHANNELS,
    enter: enter,
    status: function () { return state.status; },
    analysing: function () { return state.status === "analysing"; },
    channel: function () { return state.channel; },
    channelById: channelById,
    setChannel: setChannel,
    waitedSeconds: waitedSeconds,
    subscribe: subscribe,
    state: function () { return state; },
  };
})();
