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

// ─────────────────────────────────────────────────────────────────
//
// Three campaign types (all backed by /api/boost):
//   coordinated_post — many accounts post AI-varied messages (+ media) with
//                      mandatory keywords, staggered over a humanized window
//   engagement       — selected accounts consistently engage target accounts
//   raid             — promo in competitors' comments + engage their engagers
// Setup creates DRAFTS; the user reviews then Activates / Runs now.

// Shared "account at risk" banner — surfaces any account in cooldown / lockout
// / low health BEFORE the user launches more actions on it (error prevention).
// Returns a node, or null when all accounts are healthy. Best-effort: never
// throws, never blocks the page if accounts-all is unavailable.
// accounts-all returns a platform-keyed object ({ twitter:[], linkedin:[],
// telegram:[], smtp:[], ... }) — NOT a flat array, and the rows carry no
// `.platform` field. Flatten to a single list with `.platform` injected so
// callers can filter/inspect uniformly (smtp → "email").
function _flattenAccountsAll(res) {
  res = res || {};
  const out = [];
  for (const p of ["twitter", "linkedin", "telegram", "smtp"]) {
    for (const a of res[p] || []) out.push({ ...a, platform: p === "smtp" ? "email" : p });
  }
  return out;
}

async function _accountRiskBanner() {
  let res = null;
  try {
    res = await api("accounts-all");
  } catch {
    return null;
  }
  const accts = _flattenAccountsAll(res);
  const atRisk = accts.filter((a) => {
    const st = String(a.status || "").toLowerCase();
    const lock = String(a.lockout_state || a.lockout || "").toLowerCase();
    const health = typeof a.health_score === "number" ? a.health_score : null;
    return (
      st === "cooldown" ||
      st === "locked" ||
      st === "suspended" ||
      (lock && lock !== "none" && lock !== "ok") ||
      (health != null && health < 40)
    );
  });
  if (!atRisk.length) return null;
  const names = atRisk
    .map((a) => a.account_identifier || a.identifier || a.display_name || "account")
    .slice(0, 4)
    .join(", ");
  return el(
    "div",
    {
      role: "alert",
      style:
        "margin-bottom:12px;padding:10px 12px;border-radius:8px;border:1px solid #5a2a2a;background:#1f1414;display:flex;align-items:center;gap:10px;",
    },
    [
      el("span", { style: "font-size:16px;" }, icon("stop")),
      el("div", { style: "flex:1;font-size:12px;color:var(--red);" }, [
        el("span", { style: "font-weight:600;" }, `${atRisk.length} account(s) at risk: `),
        el(
          "span",
          {},
          `${names}${atRisk.length > 4 ? "…" : ""}. Avoid running more actions on them until they recover.`,
        ),
      ]),
      el(
        "button",
        { class: "btn btn-secondary btn-tiny", onclick: () => navigate("accounts") },
        "Review",
      ),
    ],
  );
}

const BOOST_TYPES = {
  coordinated_post: {
    icon: "megaphone",
    name: "Coordinated post",
    blurb:
      "Many accounts post AI-varied versions of one message (+ media), each with your must-include keywords.",
  },
  engagement: {
    icon: "users",
    name: "Engagement boost",
    blurb: "All selected accounts consistently like/comment/reply to target accounts' new posts.",
  },
  raid: {
    icon: "⚔️",
    name: "Raid army",
    blurb: "Promo in competitors' comment sections + engage their likers/retweeters/commenters.",
  },
};

async function _boostAccountOptions(platform) {
  // Returns [{ id, identifier, platform }] for the account picker.
  let res = null;
  try {
    res = await api("accounts-all");
  } catch {
    res = null;
  }
  const raw = _flattenAccountsAll(res);
  return raw
    .map((a) => ({
      id: a.id ?? a.account_id,
      identifier:
        a.account_identifier ||
        a.identifier ||
        a.handle ||
        a.username ||
        a.display_name ||
        a.email ||
        a.label ||
        String(a.id),
      platform: (a.platform || "").toLowerCase(),
      status: a.status,
    }))
    .filter((a) => a.id && (!platform || platform === "multi" || a.platform === platform));
}

async function renderBoostHub() {
  const wrapper = el("div", { class: "boost-hub", style: "padding:16px;" });
  setContentNode(wrapper);
  wrapper.appendChild(
    el(
      "div",
      {
        class: "page-header",
        style: "margin-bottom:10px;display:flex;justify-content:space-between;align-items:center;",
      },
      [
        el("div", {}, [
          el("h1", { style: "margin:0;font-size:18px;" }, "Boost"),
          el(
            "p",
            { class: "muted", style: "margin:4px 0 0;font-size:12px;" },
            "Coordinate all your accounts to amplify a project — posts, engagement, and competitor raids. Humanized timing + per-account caps built in.",
          ),
        ]),
        el(
          "button",
          { class: "btn btn-primary btn-tiny", onclick: () => navigate("boost/new") },
          "+ New campaign",
        ),
      ],
    ),
  );
  _accountRiskBanner()
    .then((b) => {
      if (b) wrapper.insertBefore(b, wrapper.children[1] || null);
    })
    .catch(() => {});

  // Type quick-start cards
  const types = el("div", {
    style:
      "display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px;margin-bottom:18px;",
  });
  for (const [key, t] of Object.entries(BOOST_TYPES)) {
    types.appendChild(
      el(
        "div",
        {
          class: "card",
          style:
            "border:1px solid var(--border-soft);border-radius:10px;padding:12px;cursor:pointer;background:var(--bg-card);",
          onclick: () => navigate("boost/" + key),
        },
        [
          el("div", { style: "font-size:20px;" }, t.icon),
          el("div", { style: "font-weight:600;margin-top:4px;font-size:13px;" }, t.name),
          el(
            "div",
            { class: "muted", style: "font-size:11px;margin-top:3px;line-height:1.4;" },
            t.blurb,
          ),
        ],
      ),
    );
  }
  wrapper.appendChild(types);

  wrapper.appendChild(el("h3", { style: "margin:0 0 8px;font-size:14px;" }, "Your campaigns"));
  const list = el("div", {});
  wrapper.appendChild(list);
  const res = await api("boost-list");
  if (!res || res.error) {
    list.appendChild(el("div", { class: "muted" }, `Couldn't load: ${res?.error || "error"}`));
    return;
  }
  if (!(res.campaigns || []).length) {
    list.appendChild(
      el(
        "div",
        { class: "muted", style: "font-size:12px;" },
        "No campaigns yet. Pick a type above to create one.",
      ),
    );
    return;
  }
  for (const c of res.campaigns) {
    const t = BOOST_TYPES[c.type] || { icon: "launch", name: c.type };
    const statN = (c.stats || []).reduce((s, r) => s + (r.n || 0), 0);
    list.appendChild(
      el(
        "div",
        {
          style:
            "display:flex;align-items:center;gap:10px;padding:10px;border:1px solid #22222c;border-radius:8px;margin-bottom:6px;background:#101015;cursor:pointer;",
          onclick: () => navigate("boost/" + c.id),
        },
        [
          el("div", { style: "font-size:18px;" }, t.icon),
          el("div", { style: "flex:1;" }, [
            el("div", { style: "font-size:13px;font-weight:500;" }, c.name),
            el(
              "div",
              { class: "muted", style: "font-size:11px;" },
              `${t.name} · ${c.platform} · ${c.accounts} accounts · ${statN} actions`,
            ),
          ]),
          el(
            "span",
            {
              style: `font-size:10px;padding:2px 8px;border-radius:8px;background:${c.status === "active" ? "#143d2a" : "var(--border-soft)"};color:${c.status === "active" ? "var(--green)" : "#aaa"};`,
            },
            c.status,
          ),
        ],
      ),
    );
  }
}

async function renderBoostBuilder(params = {}) {
  const preType = params.type && BOOST_TYPES[params.type] ? params.type : "coordinated_post";
  const root = el("div", { style: "padding:16px;max-width:760px;" });
  setContentNode(root);
  root.appendChild(
    el(
      "button",
      { class: "btn btn-secondary btn-tiny", onclick: () => navigate("boost") },
      "« Boost",
    ),
  );

  const state = {
    type: preType,
    platform: "twitter",
    speed: "natural",
    name: "",
    accountIds: new Set(),
    targets: "",
    baseText: "",
    keywords: "",
    topics: "",
    config: {},
  };

  const form = el("div", { style: "margin-top:12px;" });
  root.appendChild(form);

  const labelStyle = "font-size:11px;color:#9a9aa8;display:block;margin:10px 0 2px;";
  const inputStyle =
    "width:100%;padding:7px 9px;background:#0d0d12;border:1px solid var(--border-soft);border-radius:6px;color:#eee;font-size:12px;box-sizing:border-box;";

  form.appendChild(el("h1", { style: "margin:0 0 4px;font-size:18px;" }, "New Boost campaign"));

  // Type selector
  form.appendChild(el("label", { style: labelStyle }, "Type"));
  const typeRow = el("div", { style: "display:flex;gap:6px;flex-wrap:wrap;" });
  const typeBtns = {};
  for (const [key, t] of Object.entries(BOOST_TYPES)) {
    const b = el(
      "button",
      {
        class: `btn btn-tiny ${key === state.type ? "btn-primary" : "btn-secondary"}`,
        onclick: () => {
          state.type = key;
          for (const k in typeBtns)
            typeBtns[k].className = `btn btn-tiny ${k === key ? "btn-primary" : "btn-secondary"}`;
          rebuildDynamic();
        },
      },
      `${icon.for(t.icon) ? iconHtml(icon.for(t.icon)) : t.icon} ${t.name}`,
    );
    typeBtns[key] = b;
    typeRow.appendChild(b);
  }
  form.appendChild(typeRow);

  // Name
  form.appendChild(el("label", { style: labelStyle }, "Campaign name"));
  const nameInput = el("input", {
    type: "text",
    style: inputStyle,
    placeholder: "e.g. token launch blast",
    oninput: (e) => {
      state.name = e.target.value;
    },
  });
  form.appendChild(nameInput);

  // Platform + speed
  form.appendChild(el("label", { style: labelStyle }, "Platform"));
  const platSel = el(
    "select",
    {
      style: inputStyle,
      onchange: (e) => {
        state.platform = e.target.value;
        refreshAccounts();
      },
    },
    [
      el("option", { value: "twitter" }, "X / Twitter"),
      el("option", { value: "linkedin" }, "LinkedIn"),
      el("option", { value: "multi" }, "Multi"),
    ],
  );
  form.appendChild(platSel);
  form.appendChild(
    el("label", { style: labelStyle }, "Speed (humanized stagger between accounts)"),
  );
  const speedSel = el(
    "select",
    {
      style: inputStyle,
      onchange: (e) => {
        state.speed = e.target.value;
      },
    },
    [
      el("option", { value: "tight" }, "Tight (seconds — higher impact, higher risk)"),
      el("option", { value: "natural", selected: true }, "Natural (recommended)"),
      el("option", { value: "slow" }, "Slow (spread out — safest)"),
    ],
  );
  form.appendChild(speedSel);

  // Accounts picker
  form.appendChild(el("label", { style: labelStyle }, "Accounts to use"));
  const acctMount = el(
    "div",
    {
      style:
        "border:1px solid var(--border-soft);border-radius:6px;padding:8px;max-height:160px;overflow:auto;",
    },
    [el("div", { class: "muted", style: "font-size:11px;" }, "Loading accounts…")],
  );
  form.appendChild(acctMount);
  async function refreshAccounts() {
    state.accountIds.clear();
    acctMount.innerHTML = "";
    const opts = await _boostAccountOptions(state.platform);
    if (!opts.length) {
      acctMount.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size:11px;" },
          "No connected accounts for this platform.",
        ),
      );
      return;
    }
    for (const a of opts) {
      const row = el(
        "label",
        {
          style:
            "display:flex;align-items:center;gap:8px;font-size:12px;padding:3px 0;cursor:pointer;",
        },
        [
          el("input", {
            type: "checkbox",
            onchange: (e) => {
              e.target.checked ? state.accountIds.add(a) : state.accountIds.delete(a);
            },
          }),
          el("span", {}, `${a.identifier} `),
          el("span", { class: "muted", style: "font-size:10px;" }, a.platform),
        ],
      );
      acctMount.appendChild(row);
    }
  }

  // Dynamic section (targets vs content) by type
  const dynMount = el("div", {});
  form.appendChild(dynMount);
  function rebuildDynamic() {
    dynMount.innerHTML = "";
    if (state.type === "coordinated_post") {
      dynMount.appendChild(el("label", { style: labelStyle }, "Base message"));
      dynMount.appendChild(
        el("textarea", {
          style: inputStyle + "min-height:70px;",
          placeholder: "What do you want every account to post (in their own AI-varied words)?",
          oninput: (e) => {
            state.baseText = e.target.value;
          },
        }),
      );
      dynMount.appendChild(
        el(
          "label",
          { style: labelStyle },
          "Mandatory keywords (comma-separated — appear in EVERY post)",
        ),
      );
      dynMount.appendChild(
        el("input", {
          type: "text",
          style: inputStyle,
          placeholder: "$TICKER, launch, mint",
          oninput: (e) => {
            state.keywords = e.target.value;
          },
        }),
      );
      dynMount.appendChild(
        el("label", { style: labelStyle }, "Topics to vary across (comma-separated, optional)"),
      );
      dynMount.appendChild(
        el("input", {
          type: "text",
          style: inputStyle,
          placeholder: "speed, fees, community",
          oninput: (e) => {
            state.topics = e.target.value;
          },
        }),
      );
      dynMount.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size:11px;margin-top:6px;" },
          "Add media after saving (open the campaign → Upload media).",
        ),
      );
    } else if (state.type === "engagement") {
      dynMount.appendChild(
        el(
          "label",
          { style: labelStyle },
          "Target accounts to engage (comma-separated handles / LinkedIn slugs)",
        ),
      );
      dynMount.appendChild(
        el("input", {
          type: "text",
          style: inputStyle,
          placeholder: "@founder1, @founder2",
          oninput: (e) => {
            state.targets = e.target.value;
          },
        }),
      );
      dynMount.appendChild(el("label", { style: labelStyle }, "Actions"));
      const actRow = el("div", { style: "display:flex;gap:12px;font-size:12px;flex-wrap:wrap;" });
      for (const [k, lbl, def] of [
        ["like", "Like", true],
        ["comment", "AI comment", false],
        ["retweet", "Retweet", false],
      ]) {
        state.config[k] = def;
        actRow.appendChild(
          el("label", { style: "display:flex;align-items:center;gap:4px;cursor:pointer;" }, [
            el("input", {
              type: "checkbox",
              checked: def || undefined,
              onchange: (e) => {
                state.config[k] = e.target.checked;
              },
            }),
            el("span", {}, lbl),
          ]),
        );
      }
      dynMount.appendChild(actRow);
      dynMount.appendChild(el("label", { style: labelStyle }, "Pitch for AI comments (optional)"));
      dynMount.appendChild(
        el("input", {
          type: "text",
          style: inputStyle,
          placeholder: "what you'd genuinely say",
          oninput: (e) => {
            state.config.pitch = e.target.value;
          },
        }),
      );
    } else if (state.type === "raid") {
      dynMount.appendChild(
        el("label", { style: labelStyle }, "Competitors to raid (comma-separated handles / slugs)"),
      );
      dynMount.appendChild(
        el("input", {
          type: "text",
          style: inputStyle,
          placeholder: "@rival1, @rival2",
          oninput: (e) => {
            state.targets = e.target.value;
          },
        }),
      );
      dynMount.appendChild(
        el(
          "label",
          { style: labelStyle },
          "Promo message (posted in their comments — keywords enforced)",
        ),
      );
      dynMount.appendChild(
        el("textarea", {
          style: inputStyle + "min-height:60px;",
          placeholder: "your one-line promo",
          oninput: (e) => {
            state.baseText = e.target.value;
          },
        }),
      );
      dynMount.appendChild(
        el("label", { style: labelStyle }, "Mandatory keywords (comma-separated)"),
      );
      dynMount.appendChild(
        el("input", {
          type: "text",
          style: inputStyle,
          placeholder: "$TICKER, try it",
          oninput: (e) => {
            state.keywords = e.target.value;
          },
        }),
      );
      const engRow = el(
        "label",
        {
          style:
            "display:flex;align-items:center;gap:6px;font-size:12px;margin-top:8px;cursor:pointer;",
        },
        [
          el("input", {
            type: "checkbox",
            checked: true,
            onchange: (e) => {
              state.config.engageEngagers = e.target.checked;
            },
          }),
          el("span", {}, "Also engage the competitors' likers / retweeters / commenters"),
        ],
      );
      state.config.engageEngagers = true;
      dynMount.appendChild(engRow);
    }
  }

  // Save
  const saveBtn = el(
    "button",
    {
      class: "btn btn-primary",
      style: "margin-top:16px;",
      onclick: async (e) => {
        if (!state.name.trim()) {
          toast("Name the campaign first", "err");
          return;
        }
        if (!state.accountIds.size) {
          toast("Select at least one account", "err");
          return;
        }
        const split = (s) =>
          String(s || "")
            .split(",")
            .map((x) => x.trim())
            .filter(Boolean);
        const accounts = [...state.accountIds].map((a) => ({
          accountId: a.id,
          accountIdentifier: a.identifier,
          platform: a.platform || state.platform,
        }));
        const body = {
          type: state.type,
          name: state.name.trim(),
          platform: state.platform,
          speed: state.speed,
          config: state.config,
          accounts,
        };
        if (state.type === "engagement" || state.type === "raid") {
          const kind = state.type === "raid" ? "competitor" : "account";
          body.targets = split(state.targets).map((id) => ({
            kind,
            identifier: id,
            platform: state.platform,
          }));
        }
        if (state.type === "coordinated_post" || state.type === "raid") {
          body.content = {
            baseText: state.baseText,
            mandatoryKeywords: split(state.keywords),
            topics: split(state.topics),
          };
        }
        await withProgress(e.target, async () => {
          const res = await api("boost-create", { body });
          if (res?.error) {
            toast(res.error, "err");
            return;
          }
          toast("Draft created — review, upload media, then Activate", "ok");
          navigate("boost/" + res.id);
        });
      },
    },
    "Save draft",
  );
  form.appendChild(saveBtn);

  platSel.value = state.platform;
  rebuildDynamic();
  await refreshAccounts();
}

async function renderBoostDetail(params = {}) {
  const id = params.id;
  const root = el("div", { style: "padding:16px;max-width:760px;" });
  setContentNode(root);
  root.appendChild(
    el(
      "button",
      { class: "btn btn-secondary btn-tiny", onclick: () => navigate("boost") },
      "« Boost",
    ),
  );
  const mount = el("div", { style: "margin-top:12px;" }, [
    el("div", { class: "muted" }, "Loading…"),
  ]);
  root.appendChild(mount);

  // Live-poll state. Jittered bursts take minutes, so a single 4s reload reads
  // "nothing yet" and looks broken (Doherty threshold — show progress, don't
  // imply completion). We poll until the action count stops changing, capped.
  let _pollTimer = null;
  let _pollTicks = 0;
  let _lastActionTotal = -1;
  let _settleStreak = 0;
  let _polling = false;
  function stopLivePoll() {
    if (_pollTimer) {
      clearInterval(_pollTimer);
      _pollTimer = null;
    }
    _polling = false;
  }
  function startLivePoll() {
    stopLivePoll();
    _pollTicks = 0;
    _settleStreak = 0;
    _polling = true;
    load(); // immediate
    _pollTimer = setInterval(async () => {
      // Stop if the user navigated away (mirror the LI activity poller).
      if (dashboardState.currentRoute !== "boost/detail") {
        stopLivePoll();
        return;
      }
      _pollTicks++;
      if (_pollTicks > 40) {
        stopLivePoll();
        load();
        return;
      } // ~2 min cap
      await load();
    }, 3000);
  }

  async function load() {
    const res = await api("boost-get", { id });
    // Settle detection: once actions appear and then stop growing for 2 polls,
    // the burst is done — stop polling.
    if (_polling) {
      const total = (res?.stats || []).reduce((s, r) => s + (r.n || 0), 0);
      if (total === _lastActionTotal && total > 0) {
        _settleStreak++;
        if (_settleStreak >= 2) stopLivePoll();
      } else _settleStreak = 0;
      _lastActionTotal = total;
    }
    mount.innerHTML = "";
    if (!res || res.error) {
      mount.appendChild(el("div", { class: "muted" }, `Couldn't load: ${res?.error || "error"}`));
      return;
    }
    const c = res.campaign;
    const t = BOOST_TYPES[c.type] || { icon: "launch", name: c.type };

    mount.appendChild(
      el("div", { style: "display:flex;justify-content:space-between;align-items:center;" }, [
        el("div", {}, [
          el(
            "h1",
            { style: "margin:0;font-size:18px;" },
            `${icon.for(t.icon) ? iconHtml(icon.for(t.icon)) : t.icon} ${c.name}`,
          ),
          el(
            "div",
            { class: "muted", style: "font-size:12px;margin-top:2px;" },
            `${t.name} · ${c.platform} · speed: ${c.speed} · ${c.status}`,
          ),
        ]),
        el("div", { style: "display:flex;gap:6px;" }, [
          el(
            "button",
            {
              class: `btn btn-tiny ${c.status === "active" ? "btn-secondary" : "btn-primary"}`,
              onclick: async (e) => {
                await withProgress(e.target, async () => {
                  await api("boost-status", {
                    id,
                    status: c.status === "active" ? "paused" : "active",
                  });
                });
                load();
              },
            },
            c.status === "active" ? "Pause" : "Activate",
          ),
          el(
            "button",
            {
              class: "btn btn-primary btn-tiny",
              onclick: async (e) => {
                // Preview exactly what this boost will do (accounts + drafted posts) before sending.
                const __pv = await runWithPreview("boost", id);
                if (!__pv?.confirmed) return;
                await withProgress(e.target, async () => {
                  const r = await api("boost-run", { id });
                  if (r?.error) toast(r.error, "err");
                  else toast("Started — watching for actions live…", "ok");
                });
                startLivePoll();
              },
            },
            "▶ Run now",
          ),
        ]),
      ]),
    );

    // Accounts + targets summary
    mount.appendChild(
      el(
        "div",
        { style: "margin-top:12px;font-size:12px;color:#bbb;" },
        `${(res.accounts || []).length} account(s): ${
          (res.accounts || [])
            .map((a) => a.account_identifier)
            .filter(Boolean)
            .join(", ") || "—"
        }`,
      ),
    );
    if ((res.targets || []).length) {
      mount.appendChild(
        el(
          "div",
          { style: "margin-top:4px;font-size:12px;color:#bbb;" },
          `Targets: ${res.targets.map((x) => x.identifier).join(", ")}`,
        ),
      );
    }
    if (res.content) {
      if (res.content.base_text)
        mount.appendChild(
          el(
            "div",
            {
              style:
                "margin-top:8px;padding:8px;border:1px solid #22222c;border-radius:6px;background:#101015;font-size:12px;",
            },
            [
              el(
                "div",
                { class: "muted", style: "font-size:10px;margin-bottom:3px;" },
                "Base message",
              ),
              el("div", {}, res.content.base_text),
            ],
          ),
        );
      if ((res.content.mandatoryKeywords || []).length)
        mount.appendChild(
          el(
            "div",
            { style: "margin-top:4px;font-size:11px;color:#c9a96a;" },
            `Must include: ${res.content.mandatoryKeywords.join(", ")}`,
          ),
        );
      mount.appendChild(
        el(
          "div",
          { style: "margin-top:4px;font-size:11px;color:var(--text-3);" },
          `Media: ${(res.content.media || []).length} file(s)`,
        ),
      );
    }

    // Media upload (coordinated_post / raid)
    if (c.type === "coordinated_post" || c.type === "raid") {
      const fileInput = el("input", {
        type: "file",
        accept: "image/*",
        style: "display:none;",
        onchange: async (ev) => {
          const file = ev.target.files[0];
          if (!file) return;
          const buf = await file.arrayBuffer();
          let bin = "";
          const bytes = new Uint8Array(buf);
          for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
          const dataBase64 = btoa(bin);
          const r = await api("boost-media", {
            id,
            body: { filename: file.name, mimeType: file.type || "image/png", dataBase64 },
          });
          if (r?.error) toast(r.error, "err");
          else {
            toast("Media added", "ok");
            load();
          }
        },
      });
      mount.appendChild(fileInput);
      mount.appendChild(
        el(
          "button",
          {
            class: "btn btn-secondary btn-tiny",
            style: "margin-top:8px;",
            onclick: () => fileInput.click(),
          },
          "+ Upload media",
        ),
      );
    }

    // Action stats
    const stats = res.stats || [];
    const statsBox = el("div", {
      style:
        "margin-top:14px;padding:10px;border:1px solid var(--border-soft);border-radius:8px;background:var(--bg-card);",
    });
    statsBox.appendChild(
      el("div", { style: "display:flex;align-items:center;gap:8px;margin-bottom:6px;" }, [
        el("div", { style: "font-weight:600;font-size:13px;flex:1;" }, "Actions so far"),
        _polling ? el("span", { style: "font-size:10px;color:var(--green);" }, "● live") : null,
      ]),
    );
    if (!stats.length)
      statsBox.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size:12px;" },
          _polling
            ? "Running — first actions appear within a minute or two (humanized timing)…"
            : "Nothing yet. Activate or Run now.",
        ),
      );
    else
      for (const s of stats)
        statsBox.appendChild(el("div", { style: "font-size:12px;" }, `${s.action_type}: ${s.n}`));
    mount.appendChild(statsBox);

    mount.appendChild(
      el(
        "button",
        {
          class: "btn btn-secondary btn-tiny",
          style: "margin-top:14px;",
          onclick: async () => {
            if (!confirm("Archive this campaign?")) return;
            await api("boost-delete", { id });
            navigate("boost");
          },
        },
        "Archive",
      ),
    );
  }
  await load();
}

function _renderActivityAllStub() {
  // Kept as a fallback if _buildAllActivityBody isn't called; the
  // hub now wires the All tab to the unified feed, so this is rarely seen.
  return el(
    "div",
    {
      style:
        "max-width:520px;margin:32px auto;padding:24px;background:var(--bg-card);border:1px solid var(--border-soft);border-radius:10px;text-align:center;",
    },
    [
      el("div", { style: "font-size:32px;margin-bottom:8px;" }, icon("globe")),
      el(
        "h2",
        { style: "margin:0 0 8px;font-size:16px;color:var(--text-1);" },
        "Unified feed unavailable",
      ),
      el(
        "p",
        { class: "muted", style: "margin:0 0 16px;font-size:13px;line-height:1.5;" },
        "Neither LinkedIn nor Telegram event feeds responded. Open the per-platform tabs to verify each source independently.",
      ),
      el(
        "button",
        { class: "btn btn-secondary", onclick: () => navigate("action-log/linkedin") },
        "→ LinkedIn tab",
      ),
    ],
  );
}

function _renderActivityPlatformStub(platform) {
  const labels = { x: "X", gmail: "Gmail" };
  const icons = { x: "🐦", gmail: "📧" };
  const blurbs = {
    x: "X runs entirely from the in-page launcher today — there's no dashboard-side action log to surface. Once the X background-tab adapter ships (mirroring LinkedIn's worker tab + linkedin_action_log pattern), this tab will stream rows the same way.",
    gmail:
      "Email tracking events (opens / clicks / bounces) are recorded in email_tracking_events but don't have a list endpoint today. Server-side aggregation lands in a follow-up — for now, per-message tracking surfaces inside Campaigns + Sequences.",
  };
  return el(
    "div",
    {
      style:
        "max-width:520px;margin:32px auto;padding:24px;background:var(--bg-card);border:1px solid var(--border-soft);border-radius:10px;text-align:center;",
    },
    [
      el("div", { style: "font-size:32px;margin-bottom:8px;" }, icons[platform] || "📡"),
      el(
        "h2",
        { style: "margin:0 0 8px;font-size:16px;color:var(--text-1);" },
        `${labels[platform] || platform} log — coming next pass`,
      ),
      el(
        "p",
        { class: "muted", style: "margin:0 0 16px;font-size:13px;line-height:1.5;" },
        blurbs[platform] || "Endpoint not yet wired for this platform.",
      ),
      el(
        "button",
        { class: "btn btn-secondary", onclick: () => navigate("action-log/all") },
        "→ All log",
      ),
    ],
  );
}
