/* global window */

// notifications-store — the single source of truth for the notification center.
// window.buildAlerts(deals) derives ONE normalized, sorted list of noteworthy
// events from data that already lives in the app; both surfaces that show
// notifications (the header NotificationCenter and the full-page AlertsScreen)
// read from here, so their counts and content can never drift apart.
//
// A notification center is NOT just a to-do list of overdue tasks — it's the
// stream of everything worth the rep's attention. Alerts are grouped into four
// categories (see ALERT_CATEGORIES):
//   action  — commitments overdue / at-risk (things you owe or are waiting on)
//   health  — deals drifting off rhythm (lost-beat / at-risk off-beat)
//   signal  — buyer momentum: agent-surfaced signals + shared-room activity
//   win     — momentum in your favor: positive agent reads, booked openings
//
// Sources are all existing app data, reused verbatim — no new seed data. Each
// alert is shaped to be BOTH renderable AND directly consumable by
// nudgeFeedAction() (carries action/title/deal/time/body) so every CTA routes
// through the standard openActionChoice → previewAction → fireToast chain.

(function () {
  // Fixed demo "present". Deal closeDates / commitment dueDates are static
  // strings anchored here; measuring proximity against the live clock would be
  // wrong, so date math against those strings uses DEMO_NOW.
  var DEMO_NOW = new Date("2026-06-02T00:00:00");

  // Category metadata — order here is the display order everywhere. Exported so
  // the panel + page render identical labels/dots.
  var ALERT_CATEGORIES = [
    { key: "action", label: "Needs you",     hint: "Overdue & at-risk commitments", dot: "warn" },
    { key: "health", label: "Deal health",   hint: "Deals drifting off rhythm",      dot: "amber" },
    { key: "signal", label: "Buyer signals", hint: "Momentum from the buying side",  dot: "info" },
    { key: "win",    label: "Wins",          hint: "Momentum in your favor",         dot: "good" },
  ];
  var CATEGORY_ORDER = { action: 0, health: 1, signal: 2, win: 3 };
  var SEVERITY_RANK = { warn: 0, amber: 1, info: 2, good: 3 };

  function isClosed(deal) {
    return /closed|clos\b|clôtur/i.test((deal.stage || "") + " " + (deal.statusLabel || ""));
  }
  function firstName(who) { return who ? String(who).split(" ")[0] : "the buyer"; }
  function dealLabel(d) { return (d && (d.company || d.dealName || d.id)) || "this deal"; }
  function dealById(deals, id) { return deals.find(function (d) { return d.id === id; }) || null; }

  // An alert "needs you" (lights the bell badge) when it's a genuine problem:
  // warn severity and not a win. Broader than overdue — includes lost-beat
  // deals and warn buyer signals (e.g. champion departed).
  function needsYou(a) { return a.severity === "warn" && a.category !== "win"; }

  function buildAlerts(deals) {
    deals = deals || window.SEED_DEALS || [];
    var out = [];

    // 1) ACTION — commitments overdue (loud) + at-risk. The only records with a
    //    genuine deadline state; `what` is authentic to the deal.
    deals.forEach(function (d) {
      (d.commitments || []).forEach(function (c) {
        if (c.status !== "overdue" && c.status !== "at-risk") return;
        var overdue = c.status === "overdue";
        var owe = c.direction === "us-to-buyer";
        var lead = (owe ? "You owe " : "Waiting on ") + (c.who || "the buyer");
        out.push({
          id: "cmt:" + d.id + ":" + c.id,
          category: "action",
          source: "commitment",
          severity: overdue ? "warn" : "amber",
          dealId: d.id,
          dealName: dealLabel(d),
          agentId: "scout",
          ageDays: overdue ? (c.ageDays || 0) : 0,
          timeLabel: overdue
            ? (c.ageDays ? c.ageDays + "d overdue" : "Overdue")
            : "At risk · due " + (c.dueDate || "soon"),
          title: c.what,
          body: lead + (c.whoRole ? " (" + c.whoRole + ")" : "") + (c.sourceTitle ? " · " + c.sourceTitle : ""),
          deal: dealLabel(d),
          time: c.dueDate || "",
          action: overdue ? (owe ? "Send it now" : "Chase " + firstName(c.who)) : "Send reminder",
        });
      });
    });

    // 2) HEALTH — deals drifting off rhythm. lost-beat always; off-beat only
    //    when the deal is already flagged at-risk/needs-attention, so this stays
    //    meaningful instead of firing on every slightly-out-of-step deal.
    var getRhythm = window.getDealRhythm;
    var getNarr = window.getDealRhythmNarrative;
    if (typeof getRhythm === "function") {
      deals.forEach(function (d) {
        if (isClosed(d)) return;
        var beat = getRhythm(d);
        var flagged = /at-risk|needs-attention/i.test(d.status || "");
        if (beat !== "lost-beat" && !(beat === "off-beat" && flagged)) return;
        var lost = beat === "lost-beat";
        var narr = typeof getNarr === "function" ? getNarr(d) : null;
        out.push({
          id: "health:" + d.id,
          category: "health",
          source: "rhythm",
          severity: lost ? "warn" : "amber",
          dealId: d.id,
          dealName: dealLabel(d),
          agentId: "forecaster",
          ageDays: lost ? 2 : 1, // sort lost-beat above off-beat within health
          timeLabel: lost ? "Lost the beat" : "Off beat",
          title: (narr && narr.title) || (lost ? "Buyer has pulled away" : "Out of sync — needs a re-anchor"),
          body: d.statusLabel || (narr && narr.body) || "",
          deal: dealLabel(d),
          time: "now",
          action: "Re-anchor the deal",
        });
      });
    }

    // 3) SIGNAL + 4) WIN — the cross-deal agent feed. "good" reads are wins;
    //    everything else is a buyer signal. The feed already carries the agent,
    //    the human-written body, and a recommended action.
    (window.SEED_NUDGES_FEED || []).forEach(function (n) {
      var win = n.severity === "good";
      out.push({
        id: "feed:" + n.id,
        category: win ? "win" : "signal",
        source: "feed",
        severity: n.severity || "info",
        dealId: n.dealId,
        dealName: n.deal,
        agentId: n.agentId,
        ageDays: 0,
        timeLabel: n.time || "",
        title: n.title,
        body: n.body,
        deal: n.deal,
        time: n.time,
        action: n.action,
      });
    });

    // 3b) SIGNAL — shared-room activity (buyer leaning in). Take the most recent
    //     engagement events across all rooms; these are positive momentum.
    if (window.RoomSignals && typeof window.RoomSignals.list === "function") {
      var NOTABLE = { open: 1, video: 1, question: 1, agenda: 1 };
      var rooms = (window.RoomSignals.list() || [])
        .filter(function (s) { return NOTABLE[s.type]; })
        .slice(0, 4);
      rooms.forEach(function (s) {
        var d = dealById(deals, s.dealId);
        out.push({
          id: "room:" + s.id,
          category: "signal",
          source: "room",
          severity: "good", // buyer engagement is positive momentum
          dealId: s.dealId,
          dealName: d ? dealLabel(d) : (s.dealId || "Room"),
          agentId: "scout",
          ageDays: 0,
          timeLabel: (s.time || "") + " · room",
          title: (s.who ? s.who + " · " : "") + (s.title || "Room activity"),
          body: s.detail || "",
          deal: d ? dealLabel(d) : s.dealId,
          time: s.time || "",
          action: null, // no CTA — "Open" the deal is enough
        });
      });
    }

    // 4b) WIN — openings that booked a meeting or graduated to a live deal.
    //     Reads live overrides from the openings store when present.
    if (Array.isArray(window.OPENINGS)) {
      var store = window.NudgeOpenings;
      window.OPENINGS.forEach(function (o) {
        var stage = (store && store.effectiveStage) ? store.effectiveStage(o) : o.stage;
        var grad = store && store.isGraduated && store.isGraduated(o.id);
        if (stage !== "booked" && !grad) return;
        var co = o.company || (store && store.companyOf ? store.companyOf(o.id) : o.id);
        out.push({
          id: "opening:" + o.id,
          category: "win",
          source: "opening",
          severity: "good",
          dealId: null, // openings aren't deals yet
          dealName: co,
          agentId: "opener",
          ageDays: 0,
          timeLabel: grad ? "Graduated" : "Booked",
          title: grad ? co + " graduated to a live deal" : co + " booked a meeting",
          body: grad ? "Reciprocal buyer movement — handed to Deal Rhythm." : "The opener earned a first meeting.",
          deal: co,
          time: "now",
          action: null,
        });
      });
    }

    // Sort: category order → severity → age (most overdue first within a bucket).
    out.sort(function (a, b) {
      if (CATEGORY_ORDER[a.category] !== CATEGORY_ORDER[b.category]) {
        return CATEGORY_ORDER[a.category] - CATEGORY_ORDER[b.category];
      }
      if (SEVERITY_RANK[a.severity] !== SEVERITY_RANK[b.severity]) {
        return (SEVERITY_RANK[a.severity] ?? 9) - (SEVERITY_RANK[b.severity] ?? 9);
      }
      return (b.ageDays || 0) - (a.ageDays || 0);
    });
    return out;
  }

  // A short, DIVERSIFIED list for the header dropdown — up to `perCat` from each
  // category (in category order) so the panel always reflects the full range of
  // activity, never just the overdue pile. The badge conveys the true volume.
  function alertsForPanel(alerts, perCat, max) {
    perCat = perCat || 2;
    max = max || 7;
    var picked = [];
    ALERT_CATEGORIES.forEach(function (cat) {
      var n = 0;
      alerts.forEach(function (a) {
        if (a.category !== cat.key || n >= perCat || picked.length >= max) return;
        picked.push(a);
        n++;
      });
    });
    return picked;
  }

  // Counts for the badge + page headers. `readIds` is an optional Set; the
  // badge shows unread "needs you" items (warn, non-win) — broader than overdue,
  // but pure signals/wins never light the alarm.
  function summarizeAlerts(alerts, readIds) {
    alerts = alerts || [];
    var hasRead = readIds && typeof readIds.has === "function";
    var counts = { action: 0, health: 0, signal: 0, win: 0 };
    var unread = 0, needsYouUnread = 0;
    alerts.forEach(function (a) {
      var isRead = hasRead && readIds.has(a.id);
      if (!isRead) unread++;
      if (counts[a.category] != null) counts[a.category]++;
      if (needsYou(a) && !isRead) needsYouUnread++;
    });
    return {
      total: alerts.length,
      unreadCount: unread,
      needsYouUnread: needsYouUnread,
      byCategory: counts,
    };
  }

  window.buildAlerts = buildAlerts;
  window.alertsForPanel = alertsForPanel;
  window.summarizeAlerts = summarizeAlerts;
  window.alertNeedsYou = needsYou;
  window.ALERT_CATEGORIES = ALERT_CATEGORIES;
  window.ALERT_DEMO_NOW = DEMO_NOW;
})();
