// dashboard-performance-hub-2026-05-30-fro.js — extracted verbatim from dashboard.js (pure move).

// ── #performance hub (2026-05-30 from-scratch reorg, Phase B) ────
//
// Internal tab strip [Funnel | Budgets] over the unified Performance
// surface. Both tabs reuse the existing renderer bodies so behavior is
// identical — just one entry point in the sidebar instead of two
// scattered pages. Funnel = aggregate conversion + cost-per-reply;
// Budgets = per-account daily-cap utilization.
async function renderPerformanceHub({ tab } = {}) {
  const activeTab = tab || "overview";
  const wrapper = el("div", {
    class: "performance-hub",
    style: "padding:16px;max-width:1100px;margin:0 auto;",
  });
  setContentNode(wrapper);

  wrapper.appendChild(
    el("div", { class: "page-header", style: "margin-bottom:10px;" }, [
      el("h1", { style: "margin:0;font-size:18px;" }, "Performance"),
      el(
        "p",
        { class: "muted", style: "margin:4px 0 0;font-size:12px;" },
        "How your automations are converting + which accounts are pulling their weight. Funnel answers WHY; Budgets answers WHY NOT.",
      ),
    ]),
  );

  const tabs = [
    { id: "overview", icon: "📈", label: "Overview", route: "performance/overview" },
    { id: "funnel", icon: "📊", label: "Funnel", route: "performance/funnel" },
    { id: "budgets", icon: "💰", label: "Budgets", route: "performance/budgets" },
  ];
  wrapper.appendChild(_renderHubTabStrip(tabs, activeTab));

  const content = el("div", { class: "hub-content" });
  wrapper.appendChild(content);

  if (activeTab === "overview") {
    await _buildStatsOverviewBody(content);
  } else if (activeTab === "funnel") {
    await _buildWorkflowPerformanceBody(content);
  } else if (activeTab === "budgets") {
    await _buildWorkflowAccountBudgetsBody(content);
  } else {
    content.appendChild(el("div", { class: "muted" }, `Unknown tab: ${activeTab}`));
  }
}

// Centralized cross-platform analytics — ONE view over /api/stats/overview
// (action health across every platform + the outreach funnel + top actions +
// error breakdown), with a 7d/30d window toggle. Plain tiles/bars, no charting dep.
async function _buildStatsOverviewBody(root, days = 7) {
  root.innerHTML = "";
  root.appendChild(
    el("div", { style: "display:flex;gap:6px;margin-bottom:14px;align-items:center;" }, [
      el("span", { class: "muted", style: "font-size:12px;margin-right:4px;" }, "Window:"),
      ...[7, 30].map((d) =>
        el(
          "button",
          {
            class: d === days ? "btn btn-primary btn-xs" : "btn btn-secondary btn-xs",
            onclick: () => _buildStatsOverviewBody(root, d),
          },
          `${d}d`,
        ),
      ),
    ]),
  );
  const loading = el("div", { class: "muted", style: "padding:14px;" }, "Loading stats…");
  root.appendChild(loading);

  const res = await api("stats-overview", { queryString: `?days=${days}` }).catch((e) => ({
    error: e.message,
  }));
  loading.remove();
  if (res?.error || !res?.activity) {
    root.appendChild(
      el("div", { class: "muted" }, `Couldn't load stats: ${res?.error || "no data"}`),
    );
    return;
  }
  const a = res.activity,
    f = res.funnel || {};
  const totals = a.totals || { ok: 0, fail: 0, skip: 0 };
  const total = totals.ok + totals.fail + totals.skip;
  const successRate = total ? Math.round((totals.ok / total) * 100) : 0;

  const tile = (label, val, color) =>
    el(
      "div",
      {
        style:
          "flex:1;min-width:120px;background:#13131a;border:1px solid #2a2a38;border-radius:8px;padding:12px 14px;",
      },
      [
        el(
          "div",
          { style: `font-size:22px;font-weight:600;color:${color || "#fff"};` },
          String(val),
        ),
        el("div", { style: "font-size:11px;color:#888;margin-top:2px;" }, label),
      ],
    );
  const section = (t) => el("h3", { style: "font-size:13px;color:#ccc;margin:18px 0 8px;" }, t);

  root.appendChild(
    el("div", { style: "display:flex;gap:10px;flex-wrap:wrap;" }, [
      tile("Total actions", total),
      tile("✓ ok", totals.ok, "#5fdc8f"),
      tile("✗ fail", totals.fail, "#ff8b8b"),
      tile(
        "Success rate",
        successRate + "%",
        successRate >= 80 ? "#5fdc8f" : successRate >= 50 ? "#fbbf24" : "#ff8b8b",
      ),
    ]),
  );

  root.appendChild(section("By platform"));
  const pf = a.byPlatform || {};
  if (!Object.keys(pf).length)
    root.appendChild(
      el("div", { class: "muted", style: "font-size:12px;" }, "No platform activity in window."),
    );
  for (const [p, s] of Object.entries(pf)) {
    const t = (s.ok || 0) + (s.fail || 0) + (s.skip || 0);
    const ok = t ? Math.round(((s.ok || 0) / t) * 100) : 0;
    root.appendChild(
      el(
        "div",
        { style: "display:flex;align-items:center;gap:10px;margin-bottom:6px;font-size:12px;" },
        [
          el("span", { style: "width:80px;color:#ccc;" }, p),
          el(
            "div",
            { style: "flex:1;height:8px;background:#23232e;border-radius:4px;overflow:hidden;" },
            [el("div", { style: `height:100%;width:${ok}%;background:#5fdc8f;` })],
          ),
          el(
            "span",
            { style: "width:130px;text-align:right;color:#888;" },
            `${s.ok || 0} ok / ${s.fail || 0} fail`,
          ),
        ],
      ),
    );
  }

  if (f.funnel) {
    root.appendChild(section("Outreach funnel"));
    const fn = f.funnel;
    root.appendChild(
      el(
        "div",
        { style: "display:flex;gap:8px;flex-wrap:wrap;" },
        [
          ["sent", fn.sent],
          ["accepted", fn.accepted],
          ["opened", fn.opened],
          ["replied", fn.replied],
          ["converted", fn.converted],
        ].map(([k, v]) => tile(k, v || 0)),
      ),
    );
  }

  const acts = Object.values(a.byAction || {})
    .sort((x, y) => y.ok + y.fail + y.skip - (x.ok + x.fail + x.skip))
    .slice(0, 10);
  if (acts.length) {
    root.appendChild(section("Top actions"));
    for (const ac of acts) {
      const t = ac.ok + ac.fail + ac.skip;
      root.appendChild(
        el("div", { style: "display:flex;gap:10px;font-size:12px;margin-bottom:4px;" }, [
          el("span", { style: "width:64px;color:#777;" }, ac.platform),
          el(
            "span",
            {
              style: "flex:1;color:#ccc;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",
            },
            ac.action,
          ),
          el("span", { style: "color:#888;" }, `${t}${ac.fail ? ` · ${ac.fail} fail` : ""}`),
        ]),
      );
    }
  }

  const errs = Object.entries(a.byErrorCode || {})
    .sort((x, y) => y[1] - x[1])
    .slice(0, 8);
  if (errs.length) {
    root.appendChild(section("Top errors"));
    root.appendChild(
      el(
        "div",
        { style: "display:flex;gap:8px;flex-wrap:wrap;" },
        errs.map(([c, n]) =>
          el(
            "span",
            {
              style:
                "background:#3a1414;color:#ff8b8b;border-radius:4px;padding:3px 8px;font-size:11px;",
            },
            `${c} ×${n}`,
          ),
        ),
      ),
    );
  }
  root.appendChild(
    el(
      "div",
      { class: "muted", style: "margin-top:16px;font-size:11px;" },
      `Generated ${res.generatedAt || ""}`,
    ),
  );
}

// "Funnel" body — funnel + cost-per-reply + per-template + per-account
// + per-track breakdowns. Shared between the legacy standalone page
// (above) and the #performance/funnel tab inside renderPerformanceHub.
async function _buildWorkflowPerformanceBody(root) {
  const [costR, tplR, acctR, trackR] = await Promise.all([
    api("composition-performance-cost").catch(() => null),
    api("composition-performance-templates").catch(() => null),
    api("composition-performance-accounts").catch(() => null),
    api("composition-performance-tracks").catch(() => null),
  ]);

  root.appendChild(
    el("div", { style: "margin-bottom: 18px;" }, [
      el("h2", { style: "margin: 0; font-size: 16px; color: #fff;" }, "📊 Funnel"),
      el(
        "div",
        { class: "muted", style: "font-size: 12px; margin-top: 4px;" },
        "Aggregated funnel data across all your compositions. Helps answer: which TEMPLATE converts? which ACCOUNT pulls weight? what's my cost-per-reply?",
      ),
    ]),
  );

  // 1) Cost-per-reply hero card
  if (costR) {
    const hero = el("div", { class: "card", style: "margin-bottom: 18px; padding: 18px;" }, [
      el(
        "div",
        { style: "font-weight: 500; font-size: 13px; margin-bottom: 12px;" },
        "💰 Cost-per-reply",
      ),
      el("div", { style: "display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;" }, [
        _statCard("Attempted", String(costR.attempted || 0)),
        _statCard("Sent", String(costR.sent || 0)),
        _statCard("Replied", String(costR.replied || 0)),
        _statCard(
          "Attempts/reply",
          costR.attempts_per_reply == null ? "—" : costR.attempts_per_reply.toFixed(1),
        ),
      ]),
      costR.attempts_per_reply != null
        ? el(
            "div",
            { class: "muted", style: "font-size: 11px; margin-top: 10px; line-height: 1.5;" },
            `Each reply costs ${costR.attempts_per_reply.toFixed(1)} outbound attempts on average. Lower = better template/audience fit. Aim for <20 on cold flows, <5 on warm flows.`,
          )
        : el(
            "div",
            { class: "muted", style: "font-size: 11px; margin-top: 10px;" },
            "No replies recorded yet. Run a few compositions and check back.",
          ),
    ]);
    root.appendChild(hero);
  }

  // 2) Per-template performance
  const tplItems = (tplR?.templates || [])
    .slice()
    .sort((a, b) => (b.funnel?.replied || 0) - (a.funnel?.replied || 0));
  if (tplItems.length === 0) {
    root.appendChild(
      el("div", { class: "card", style: "padding: 16px; margin-bottom: 18px;" }, [
        el(
          "div",
          { style: "font-weight: 500; font-size: 13px; margin-bottom: 8px;" },
          "📋 Per-template performance",
        ),
        el(
          "div",
          { class: "muted", style: "font-size: 11px;" },
          "No template runs yet. Apply a template and run it to start collecting data.",
        ),
      ]),
    );
  } else {
    const tplCard = el("div", { class: "card", style: "padding: 16px; margin-bottom: 18px;" }, [
      el(
        "div",
        { style: "font-weight: 500; font-size: 13px; margin-bottom: 12px;" },
        "📋 Per-template performance",
      ),
    ]);
    for (const e of tplItems) {
      const f = e.funnel || {};
      const r = e.ratios || {};
      tplCard.appendChild(
        el(
          "div",
          {
            style: "padding: 10px 0; border-bottom: 1px solid #1a1a1a;",
          },
          [
            el(
              "div",
              {
                class: "row",
                style: "justify-content: space-between; align-items: center; margin-bottom: 6px;",
              },
              [
                el("div", { style: "font-size: 12px; font-weight: 500;" }, [
                  e.template_id,
                  el(
                    "span",
                    { class: "muted", style: "font-size: 10px; margin-left: 6px;" },
                    `· ${e.composition_count} composition${e.composition_count === 1 ? "" : "s"}`,
                  ),
                ]),
                el(
                  "div",
                  { style: "font-size: 11px; color: #5ada5a;" },
                  r.attempts_per_reply == null
                    ? "—"
                    : `${r.attempts_per_reply.toFixed(1)} attempts/reply`,
                ),
              ],
            ),
            el(
              "div",
              { class: "muted", style: "font-size: 10px;" },
              `matched: ${f.matched || 0} → eligible: ${f.eligible || 0} → attempted: ${f.attempted || 0} → sent: ${f.sent || 0} → replied: ${f.replied || 0} → converted: ${f.converted || 0}`,
            ),
          ],
        ),
      );
    }
    root.appendChild(tplCard);
  }

  // 3) Per-track aggregation (compact)
  const trackItems = (trackR?.tracks || [])
    .slice()
    .sort((a, b) => (b.funnel?.replied || 0) - (a.funnel?.replied || 0));
  if (trackItems.length > 0) {
    const trackCard = el("div", { class: "card", style: "padding: 16px; margin-bottom: 18px;" }, [
      el(
        "div",
        { style: "font-weight: 500; font-size: 13px; margin-bottom: 12px;" },
        "🗂 Per-track performance",
      ),
    ]);
    for (const t of trackItems) {
      const f = t.funnel || {};
      const r = t.ratios || {};
      trackCard.appendChild(
        el(
          "div",
          {
            style: "padding: 8px 0; border-bottom: 1px solid #1a1a1a; font-size: 11px;",
          },
          [
            el("div", { class: "row", style: "justify-content: space-between;" }, [
              el("strong", {}, t.track),
              el(
                "span",
                { class: "muted" },
                `${t.template_count} tpl · ${t.composition_count} comp · ${f.replied || 0} replies${r.reply_rate ? ` · ${(r.reply_rate * 100).toFixed(1)}%` : ""}`,
              ),
            ]),
          ],
        ),
      );
    }
    root.appendChild(trackCard);
  }

  // 4) Per-account performance
  const acctItems = (acctR?.accounts || [])
    .slice()
    .sort((a, b) => (b.funnel?.replied || 0) - (a.funnel?.replied || 0));
  if (acctItems.length > 0) {
    const acctCard = el("div", { class: "card", style: "padding: 16px;" }, [
      el(
        "div",
        { style: "font-weight: 500; font-size: 13px; margin-bottom: 12px;" },
        "🪪 Per-account performance",
      ),
    ]);
    for (const a of acctItems) {
      const f = a.funnel || {};
      const r = a.ratios || {};
      acctCard.appendChild(
        el(
          "div",
          {
            style: "padding: 8px 0; border-bottom: 1px solid #1a1a1a; font-size: 11px;",
          },
          [
            el("div", { class: "row", style: "justify-content: space-between;" }, [
              el("strong", {}, `${a.platform}@${a.account_id}`),
              el(
                "span",
                { class: "muted" },
                `attempted ${f.attempted || 0} · sent ${f.sent || 0} · replied ${f.replied || 0}${r.reply_rate ? ` (${(r.reply_rate * 100).toFixed(1)}%)` : ""}`,
              ),
            ]),
          ],
        ),
      );
    }
    root.appendChild(acctCard);
  }
}
