// dashboard-mission-control.js — extracted verbatim from dashboard.js (pure move).

// ── Mission Control ─────────────────────────────────────────────

// \u2500\u2500 Today operator console (gamification) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
// Renders the /api/today digest as actionable cards: momentum, what-needs-you,
// setup quests, feature adoption, reply-rate, goal progress, account strength.
// No XP/badges \u2014 momentum + guided adoption + outcome improvement. Returns null
// if the digest is unavailable (the rest of Today still renders).
function _miniBar(pct, color) {
  return el(
    "div",
    { style: "height:6px;border-radius:4px;background:var(--bg-3);overflow:hidden;" },
    [
      el("div", {
        style: `height:100%;width:${Math.max(0, Math.min(100, pct))}%;background:${color};`,
      }),
    ],
  );
}
function _consoleCard(title, bodyNodes, opts = {}) {
  return el("div", { class: "card", style: `padding:14px 16px;${opts.style || ""}` }, [
    el(
      "div",
      {
        style:
          "font-size:11px;color:var(--text-2);text-transform:uppercase;letter-spacing:0.05em;font-weight:600;margin-bottom:10px;",
      },
      title,
    ),
    ...bodyNodes,
  ]);
}
function buildTodayConsole(d) {
  if (!d || typeof d !== "object" || d.error) return null;

  // 1. Momentum
  const target = d.dailyTarget || 20;
  const pct = target ? Math.round(((d.todayActions || 0) / target) * 100) : 0;
  const trend = d.weekTrend || 0;
  const trendStr =
    trend > 0
      ? `\u25b2 ${trend} vs last week`
      : trend < 0
        ? `\u25bc ${Math.abs(trend)} vs last week`
        : "level vs last week";
  const momentum = _consoleCard("Momentum", [
    el(
      "div",
      { style: "display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:8px;" },
      [
        el("div", {}, [
          el(
            "span",
            { style: "font-size:24px;font-weight:700;color:var(--text-1);" },
            String(d.streak || 0),
          ),
          el(
            "span",
            { style: "font-size:12px;color:var(--text-2);margin-left:4px;" },
            `day streak${(d.streak || 0) > 0 ? " \ud83d\udd25" : ""}`,
          ),
        ]),
        el("div", { style: "flex:1;min-width:140px;" }, [
          el(
            "div",
            { style: "font-size:12px;color:var(--text-1);margin-bottom:4px;" },
            `${d.todayActions || 0} / ${target} actions today`,
          ),
          _miniBar(pct, pct >= 100 ? "#22c55e" : "#7c6bff"),
        ]),
        el(
          "div",
          {
            style: `font-size:11px;color:${trend > 0 ? "var(--green)" : trend < 0 ? "var(--red)" : "#9ca3af"};`,
          },
          trendStr,
        ),
      ],
    ),
  ]);

  // 2. Needs you now (compact triage chips)
  const fq = Array.isArray(d.focusQueue) ? d.focusQueue : [];
  const needs = fq.length
    ? _consoleCard(`Needs you now (${fq.length})`, [
        el(
          "div",
          { style: "display:flex;flex-direction:column;gap:6px;" },
          fq.map((f) =>
            el(
              "a",
              {
                href: `#${f.route}`,
                onclick: (e) => {
                  e.preventDefault();
                  navigate(f.route);
                },
                style:
                  "display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;background:var(--bg-card);color:#e8e8ef;text-decoration:none;font-size:13px;",
              },
              [
                el("span", {}, f.icon || "\u2022"),
                el("span", { style: "flex:1;" }, f.label),
                el("span", { style: "color:#7c6bff;font-size:11px;" }, "Go \u2192"),
              ],
            ),
          ),
        ),
      ])
    : null;

  // 2b. Community quests (the Circuit × Engine seam) — only present when the
  // seam has ever been used (today-digest omits it otherwise).
  const q = d.quests;
  const questsCard = q
    ? _consoleCard("Community quests", [
        el(
          "div",
          { style: "display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:8px;" },
          [
            el("div", {}, [
              el(
                "span",
                { style: "font-size:24px;font-weight:700;color:var(--text-1);" },
                String(q.xpWeek || 0),
              ),
              el(
                "span",
                { style: "font-size:12px;color:var(--text-2);margin-left:4px;" },
                "XP this week",
              ),
            ]),
            el("div", { style: "font-size:12px;color:var(--text-1);" }, [
              el("div", {}, `${q.pending || 0} queued · ${q.watching || 0} awaiting proof`),
              el(
                "div",
                { style: "color:var(--text-2);margin-top:2px;" },
                `${q.verifiedToday || 0} verified today${q.xpToday ? ` (+${q.xpToday} XP)` : ""}`,
              ),
            ]),
          ],
        ),
        el("div", {}, [
          el(
            "a",
            {
              href: "#earn",
              onclick: (e) => {
                e.preventDefault();
                navigate("earn");
              },
              style: "font-size:12px;color:#7c6bff;text-decoration:none;",
            },
            "Open Earn →",
          ),
        ]),
      ])
    : null;

  // 3. Setup quests
  const setup = d.setup || {};
  const setupCard =
    setup.total && setup.completed < setup.total
      ? _consoleCard("Finish setup", [
          el(
            "div",
            { style: "font-size:12px;color:var(--text-1);margin-bottom:6px;" },
            `${setup.completed}/${setup.total} done${setup.nextStep ? ` \u2014 next: ${setup.nextStep}` : ""}`,
          ),
          _miniBar(Math.round((setup.completed / setup.total) * 100), "#22c55e"),
          el("div", { style: "margin-top:8px;" }, [
            el(
              "a",
              {
                href: "#objectives",
                onclick: (e) => {
                  e.preventDefault();
                  navigate("objectives");
                },
                style: "font-size:12px;color:#7c6bff;text-decoration:none;",
              },
              "Continue setup \u2192",
            ),
          ]),
        ])
      : null;

  // 4. Try this (adoption)
  const adoption = Array.isArray(d.adoption) ? d.adoption : [];
  const tryCard = adoption.length
    ? _consoleCard("Try this", [
        el(
          "div",
          { style: "display:flex;flex-direction:column;gap:8px;" },
          adoption.slice(0, 3).map((a) =>
            el(
              "a",
              {
                href: `#${a.route}`,
                onclick: (e) => {
                  e.preventDefault();
                  navigate(a.route);
                },
                style:
                  "display:block;padding:8px 10px;border:1px solid var(--border-soft);border-radius:8px;color:#e8e8ef;text-decoration:none;",
              },
              [
                el("div", { style: "font-size:13px;font-weight:600;" }, `${a.label} \u2192`),
                el(
                  "div",
                  { style: "font-size:11px;color:var(--text-2);margin-top:2px;" },
                  a.why || "",
                ),
              ],
            ),
          ),
        ),
      ])
    : null;

  // 5. Outcomes & reply rate
  const oc = d.outcomes;
  const outcomesCard = oc
    ? _consoleCard("Reply rate (30d)", [
        el("div", { style: "display:flex;align-items:baseline;gap:10px;margin-bottom:6px;" }, [
          el(
            "span",
            { style: "font-size:22px;font-weight:700;color:var(--text-1);" },
            `${oc.replyRatePct}%`,
          ),
          el(
            "span",
            { style: "font-size:11px;color:var(--text-2);" },
            `${oc.replied}/${oc.sent} sent \u00b7 ${oc.converted} converted`,
          ),
        ]),
        _miniBar(
          Math.min(100, (oc.replyRatePct || 0) * 5),
          oc.replyRatePct >= 8 ? "#22c55e" : "#fbbf24",
        ),
        oc.tip
          ? el(
              "div",
              { style: "font-size:11px;color:#fbbf24;margin-top:8px;line-height:1.5;" },
              `\ud83d\udca1 ${oc.tip}`,
            )
          : null,
      ])
    : null;

  // 6. Goal progress
  const goals = Array.isArray(d.goals) ? d.goals : [];
  const goalsCard = goals.length
    ? _consoleCard("Goal progress", [
        el(
          "div",
          { style: "display:flex;flex-direction:column;gap:8px;" },
          goals.map((g) =>
            el(
              "a",
              {
                href: "#objectives",
                onclick: (e) => {
                  e.preventDefault();
                  navigate("objectives");
                },
                style: "text-decoration:none;color:inherit;display:block;",
              },
              [
                el(
                  "div",
                  {
                    style:
                      "display:flex;justify-content:space-between;font-size:12px;color:var(--text-1);margin-bottom:3px;",
                  },
                  [
                    el("span", {}, `${g.icon} ${g.name}`),
                    el("span", { style: "color:var(--text-2);" }, `${g.pct}%`),
                  ],
                ),
                _miniBar(g.pct, "#7c6bff"),
              ],
            ),
          ),
        ),
      ])
    : null;

  // 7. Account strength
  const acc = Array.isArray(d.accountStrength) ? d.accountStrength : [];
  const accCard = acc.length
    ? _consoleCard("Account strength", [
        el(
          "div",
          { style: "display:flex;flex-direction:column;gap:6px;" },
          acc.map((a) => {
            const st = a.strength == null ? null : a.strength;
            const color =
              st == null ? "#9ca3af" : st >= 70 ? "#22c55e" : st >= 50 ? "#fbbf24" : "#f87171";
            return el("div", { style: "display:flex;align-items:center;gap:8px;font-size:12px;" }, [
              el(
                "span",
                { style: "width:64px;color:var(--text-2);text-transform:capitalize;" },
                a.platform || "\u2014",
              ),
              el(
                "span",
                {
                  style:
                    "flex:1;color:#e8e8ef;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",
                },
                a.identifier || "",
              ),
              el(
                "span",
                { style: `color:${color};font-weight:600;` },
                st == null ? a.status || "\u2014" : `${st}%`,
              ),
            ]);
          }),
        ),
        el("div", { style: "margin-top:8px;" }, [
          el(
            "a",
            {
              href: "#accounts",
              onclick: (e) => {
                e.preventDefault();
                navigate("accounts");
              },
              style: "font-size:12px;color:#7c6bff;text-decoration:none;",
            },
            "Manage accounts \u2192",
          ),
        ]),
      ])
    : null;

  const cards = [
    momentum,
    needs,
    questsCard,
    setupCard,
    tryCard,
    outcomesCard,
    goalsCard,
    accCard,
  ].filter(Boolean);
  if (!cards.length) return null;
  return el(
    "div",
    {
      style:
        "display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin:14px 0;",
    },
    cards,
  );
}

async function renderMission() {
  showLoading("Loading today\u2026");

  // Stamp the visit timestamp (for the "since you were here" delta)
  // BEFORE we fetch, so we capture the value at page-open time. Mirror
  // the renderAutomations pattern so the cockpit and Today share the
  // same delta semantics.
  const TODAY_LAST_VISIT_KEY = "today:lastVisitAt";
  const lastVisitAt = localStorage.getItem(TODAY_LAST_VISIT_KEY) || null;
  try {
    localStorage.setItem(TODAY_LAST_VISIT_KEY, new Date().toISOString());
  } catch {}

  // Pull mission actions + everything the cockpit needs in parallel.
  // Each independently catches errors so a single failure doesn't blank
  // the page (the user reported "dashboard disappears" \u2014 that was the
  // previous version's behavior on the slimmest API failure).
  const [missionResp, opsLive, outcomesToday, outcomesSince, inboxResp, runsSummary, todayDigest] =
    await Promise.all([
      api("mission-control").catch(() => ({})),
      api("ops-live").catch(() => ({})),
      api("ops-outcomes", { queryString: "?days=1" }).catch(() => ({})),
      lastVisitAt
        ? api("ops-outcomes", { queryString: `?since=${encodeURIComponent(lastVisitAt)}` }).catch(
            () => ({}),
          )
        : Promise.resolve(null),
      api("inbox-replies").catch(() => ({})),
      api("automations-runs-summary", { days: 1 }).catch(() => ({})),
      // Operator console digest — fail-fast (5s) so Today never blocks on it.
      // Notably guards the deploy window where the extension is updated but the
      // bot hasn't restarted yet (the /api/today route would otherwise hang ~30s).
      Promise.race([
        api("today").catch(() => null),
        new Promise((r) => setTimeout(() => r(null), 5000)),
      ]),
    ]);
  const actions = missionResp?.actions || [];
  const urgencyColors = { high: "var(--red)", medium: "var(--yellow)", low: "var(--text-4)" };

  // \u2500\u2500 Page header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
  const header = el("div", { class: "page-header" }, [
    el("div", {}, [
      el("div", { class: "page-title" }, "Today"),
      el(
        "div",
        { class: "page-subtitle" },
        "What's running, what needs you, and what just happened.",
      ),
    ]),
    el(
      "button",
      { class: "btn btn-secondary", type: "button", onclick: renderMission },
      "\u21bb Refresh",
    ),
  ]);

  // \u2500\u2500 Status strip (reuse from Automations cockpit) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
  // Same builder as the Automations page, so Today gets the live dot,
  // sent/replied/rate, and "since you were here" delta for free.
  const statusStrip =
    typeof buildAutomationStatusStrip === "function"
      ? buildAutomationStatusStrip(opsLive, outcomesToday, outcomesSince, lastVisitAt)
      : null;

  // \u2500\u2500 Replies waiting card (highest emotional payload) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
  const replies = Array.isArray(inboxResp?.replies) ? inboxResp.replies : [];
  let repliesCard = null;
  if (replies.length > 0) {
    const fresh = freshInboundReplies(replies, lastVisitAt);
    if (fresh.length > 0) {
      const sample = fresh[0];
      const who = sample.contact_name || sample.contact_handle || "someone";
      const cutoffLabel = lastVisitAt ? "since you were here" : "in the last 24h";
      repliesCard = el(
        "div",
        {
          class: "card",
          style:
            "padding:14px 16px;margin-bottom:12px;border-left:3px solid var(--green);cursor:pointer;",
          onclick: () => navigate("inbox"),
        },
        [
          el("div", { style: "display:flex;align-items:center;gap:10px;" }, [
            el("span", { style: "font-size:20px;" }, "\u2709"),
            el("div", { style: "flex:1;" }, [
              el(
                "div",
                { style: "font-size:14px;font-weight:600;color:#86efac;" },
                fresh.length === 1
                  ? `1 new reply ${cutoffLabel}`
                  : `${fresh.length} new replies ${cutoffLabel}`,
              ),
              el("div", { class: "muted small", style: "margin-top:2px;" }, `Latest from ${who}`),
            ]),
            el(
              "button",
              {
                class: "btn btn-tiny btn-primary",
                type: "button",
                onclick: (e) => {
                  e.stopPropagation();
                  navigate("inbox");
                },
              },
              "Open inbox \u2192",
            ),
          ]),
        ],
      );
    }
  }

  // \u2500\u2500 Pinned shortcuts strip (reuse from Automations cockpit) \u2500\u2500\u2500\u2500\u2500\u2500\u2500
  // Stash render context so resolvePinTarget works (matches what
  // renderAutomations does). Without this, pinned chips show as missing.
  if (typeof window.__automationsRenderCtx === "undefined") {
    // Best-effort empty context \u2014 pinned chips will render as "missing"
    // until the user opens Automations once. Acceptable degradation.
    window.__automationsRenderCtx = {
      presetById: {},
      customById: {},
      platformById: {},
      operationById: {},
      compositionById: {},
    };
  }
  const shortcutsMount =
    typeof buildCockpitShortcuts === "function" ? buildCockpitShortcuts() : null;

  // \u2500\u2500 What needs you (mission actions + replies-waiting unified) \u2500\u2500\u2500\u2500
  const needsYouCards = [];
  for (const a of actions) {
    const actionLabels = {
      signals: "\ud83d\udd25 Review Signals",
      replies: "\ud83d\udcec Open Inbox",
      accounts: "\ud83d\udc65 Fix Accounts",
      "follow-up": "\ud83d\uddc2 Open CRM",
      capacity: "\ud83c\udfe0 View Home",
    };
    needsYouCards.push(
      el(
        "div",
        {
          class: "card",
          style: `padding: 14px 16px; margin-bottom: 8px; border-left: 3px solid ${urgencyColors[a.urgency] || "var(--border)"};`,
        },
        [
          el("div", { style: "display: flex; align-items: center; gap: 10px;" }, [
            el("span", { style: "font-size: 18px;" }, a.icon),
            el("div", { style: "flex:1;min-width:0;" }, [
              el(
                "div",
                { style: "font-size: 13px; font-weight: 600; color: var(--text-1);" },
                a.title,
              ),
              el("div", { class: "muted small" }, a.hint),
            ]),
            a.count ? el("span", { class: "badge badge-red" }, String(a.count)) : null,
            el(
              "button",
              {
                class: "btn btn-tiny btn-primary",
                type: "button",
                onclick: () => navigate(a.route || "home"),
              },
              actionLabels[a.type] || "\u2192 Go",
            ),
          ]),
        ],
      ),
    );
  }

  // \u2500\u2500 Today's activity recap (runs summary) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
  const summary = (runsSummary && runsSummary.summary) || null;
  let activityRecap = null;
  if (summary && (summary.total_runs > 0 || summary.error > 0)) {
    const totalRuns = Number(summary.total_runs) || 0;
    const success = Number(summary.success) || 0;
    const errored = Number(summary.error) || 0;
    const itemsKept = Number(summary.items_kept_total) || 0;
    const successPct = totalRuns > 0 ? Math.round((success / totalRuns) * 100) : null;
    activityRecap = el(
      "div",
      {
        class: "card",
        style: "padding:14px 16px;margin-bottom:12px;",
      },
      [
        el("div", { style: "display:flex;align-items:baseline;gap:10px;margin-bottom:10px;" }, [
          el(
            "span",
            { style: "font-size:14px;font-weight:600;color:var(--text-1);" },
            "Today's automation activity",
          ),
          el("span", { class: "muted small" }, "last 24h"),
        ]),
        el(
          "div",
          {
            style:
              "display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:12px;",
          },
          [
            statCard("Runs", totalRuns, successPct !== null ? `${successPct}% ok` : "", null),
            statCard("Items kept", itemsKept, "across all customs", null),
            statCard(
              "Errors",
              errored,
              errored > 0 ? "needs attention" : "all clean",
              errored > 0 ? "warning" : null,
            ),
          ],
        ),
      ],
    );
  }

  // \u2500\u2500 Quick links footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
  const quickLinks = el("div", { style: "display:flex;gap:8px;flex-wrap:wrap;margin-top:8px;" }, [
    el(
      "button",
      { class: "btn btn-secondary", type: "button", onclick: () => navigate("inbox") },
      "\ud83d\udcec Inbox",
    ),
    el(
      "button",
      { class: "btn btn-secondary", type: "button", onclick: () => navigate("automations") },
      "\u2699 Automations",
    ),
    el(
      "button",
      { class: "btn btn-secondary", type: "button", onclick: () => navigate("crm") },
      "\ud83d\uddc2 CRM",
    ),
    el(
      "button",
      { class: "btn btn-secondary", type: "button", onclick: () => navigate("scoreboard") },
      "\ud83c\udfc6 Scoreboard",
    ),
    el(
      "button",
      { class: "btn btn-secondary", type: "button", onclick: () => navigate("home") },
      "\ud83d\uddfa Live map",
    ),
  ]);

  // \u2500\u2500 Compose the page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
  const content = el("div", {}, [
    header,
    statusStrip,
    buildTodayConsole(todayDigest),
    repliesCard,
    needsYouCards.length > 0
      ? el("div", {}, [
          el(
            "div",
            {
              style:
                "font-size:11px;color:var(--text-2);text-transform:uppercase;letter-spacing:0.05em;font-weight:600;margin:14px 0 8px;",
            },
            `Needs your attention (${needsYouCards.length})`,
          ),
          ...needsYouCards,
        ])
      : null,
    shortcutsMount,
    activityRecap,
    needsYouCards.length === 0 && !repliesCard && !activityRecap
      ? el("div", { class: "empty-state", style: "margin-top:14px;" }, [
          el("div", { class: "empty-state-icon" }, "\u2705"),
          el("div", { class: "empty-state-title" }, "All clear!"),
          el(
            "div",
            { class: "empty-state-hint" },
            "Nothing urgent right now. Keep an eye on the activity recap above, or jump into Automations to start something.",
          ),
        ])
      : null,
    quickLinks,
  ]);
  setContentNode(content);
}
