/**
 * Clients roster — the agency "screener" view (#clients).
 *
 * One dense, sortable row per customer. This is the surface an operator scans
 * to answer "is everything running for everyone, and if not, why not" without
 * opening anything. Modelled on the screener tables crypto operators already
 * live in (Dexscreener/Nansen) rather than a card grid: scanning beats reading.
 *
 * The load-bearing column is `nextAction` — "why is this client idle". A count
 * of zero tells you something is wrong but not what; the reason column tells
 * you whether to wait, reconnect an account, or activate an automation.
 *
 * Data: GET /api/clients/overview (db/clients.js listClientsOverview).
 * `campaignCount`/`activeAutomations` count AUTOMATIONS only; pipeline rows are
 * reported separately as `pipelineRows` — they are not the same thing and were
 * once summed together, which is why the roster and the console disagreed.
 */

/** Relative "3m ago" / "2h ago" / "5d ago"; null → em dash. */
function _crAgo(ts) {
  if (!ts) return "—";
  const t = new Date(
    String(ts).replace(" ", "T") + (String(ts).endsWith("Z") ? "" : "Z"),
  ).getTime();
  if (isNaN(t)) return "—";
  const s = Math.max(0, Math.floor((Date.now() - t) / 1000));
  if (s < 60) return `${s}s ago`;
  if (s < 3600) return `${Math.floor(s / 60)}m ago`;
  if (s < 86400) return `${Math.floor(s / 3600)}h ago`;
  return `${Math.floor(s / 86400)}d ago`;
}

/**
 * The status grammar, used identically here and on the client page:
 * green = working, amber = needs you, grey = idle/nothing to do.
 */
function _crStateDot(state) {
  const tone =
    state === "active"
      ? "var(--status-ok)"
      : state === "archived"
        ? "#6b7280"
        : "var(--status-warn)";
  return el("span", {
    style: `display:inline-block;width:8px;height:8px;border-radius:50%;background:${tone};margin-right:8px;vertical-align:middle;`,
    title: state,
  });
}

/**
 * "Why is nothing happening for this client." Derived only from fields the
 * roster actually has — deliberately NOT guessing at rate limits or account
 * health, which arrive with the limits aggregate in a later phase. An honest
 * "no automations" beats a confident wrong reason.
 */
function _crNextAction(c) {
  if (c.archived) return { text: "archived", tone: "muted" };
  if (!c.activeAutomations) {
    // Only now is a missing sender roster the most useful thing to say. Checked
    // AFTER the automation count on purpose: account_clients is an optional
    // labelling table, not the metric path, so a live client with real recent
    // activity can legitimately have zero rows in it — leading with "no accounts
    // assigned" there would be a confident wrong answer. Verified against real
    // data: the one live client has 12 active automations and 0 account_clients.
    if (!c.accountCount) return { text: "no automations, no accounts assigned", tone: "warn" };
    return c.pipelineRows
      ? { text: `${c.pipelineRows} pipeline rows, 0 automations`, tone: "warn" }
      : { text: "nothing activated yet", tone: "warn" };
  }
  if (!c.lastActionAt) return { text: "activated, no runs yet", tone: "warn" };
  return { text: `last action ${_crAgo(c.lastActionAt)}`, tone: "ok" };
}

function _crToneColor(tone) {
  if (tone === "ok") return "var(--status-ok)";
  if (tone === "warn") return "var(--status-warn)";
  return "var(--text-dim, #9aa4b2)";
}

/** Pause every automation attached to one client, from the roster. */
async function _crPauseAll(client, onDone) {
  const res = await api("client-console", { id: client.id }).catch((e) => ({
    error: e?.message || "unreachable",
  }));
  if (res?.error) {
    toast(`${res.error}`, "err");
    return;
  }
  const live = (res.automations || []).filter((a) => a.state === "live");
  if (!live.length) {
    toast("Nothing running for this client", "info");
    return;
  }
  if (
    !window.confirm(
      `Pause ${live.length} automation${live.length === 1 ? "" : "s"} for ${client.name}?`,
    )
  )
    return;
  let paused = 0;
  const failed = [];
  for (const a of live) {
    const r = await api("campaigns-action", { body: { id: a.id, action: "pause" } }).catch((e) => ({
      error: e?.message || "failed",
    }));
    if (r && r.ok) paused++;
    else failed.push(`${a.name}: ${r?.error || "failed"}`);
  }
  // Report what actually happened per item — a blanket "done" after a partial
  // failure is how an operator ends up believing a client is paused when it isn't.
  if (failed.length) {
    toast(`Paused ${paused}/${live.length}. Failed: ${failed.join("; ")}`, "err", 9000);
  } else {
    toast(`Paused ${paused} automation${paused === 1 ? "" : "s"} for ${client.name}`, "ok");
  }
  if (onDone) onDone();
}

/** "+ New client" — replaces the two chained window.prompt() calls. */
function _crNewClientModal(onCreated) {
  const nameInput = el("input", {
    class: "input",
    type: "text",
    placeholder: "Client name (e.g. Acme Corp)",
    style: "width:100%;margin-bottom:10px;",
  });
  const colorInput = el("input", {
    class: "input",
    type: "color",
    value: "#4ade80",
    style: "width:64px;height:34px;padding:2px;",
  });
  openModal({
    title: "New client",
    body: el("div", {}, [
      el("div", { class: "muted" }, "Name"),
      nameInput,
      el("div", { class: "muted", style: "margin-top:10px;" }, "Colour (used on the report cover)"),
      colorInput,
    ]),
    confirmLabel: "Create",
    onConfirm: async () => {
      const name = nameInput.value.trim();
      if (!name) {
        toast("Name is required", "err");
        return false;
      }
      const res = await api("client-create", {
        body: { name, color: colorInput.value },
      }).catch((e) => ({ error: e?.message || "failed" }));
      if (res?.error) {
        toast(`${res.error}`, "err");
        return false;
      }
      toast(`Created ${name}`, "ok");
      if (onCreated) onCreated();
      return true;
    },
  });
}

async function renderClientsRoster() {
  showLoading("Loading clients…");
  const res = await api("clients-overview").catch((e) => ({
    error: e?.message || "unreachable",
  }));
  if (res?.error) {
    showError(res.error, renderClientsRoster);
    return;
  }
  const clients = Array.isArray(res?.clients) ? res.clients : [];
  setNavCapability("hasClients", clients.length > 0);

  // Build into a detached node and hand it to setContentNode(), which owns
  // the real #content container. There is no #main element in the page.
  const main = el("div", {});
  main.appendChild(
    el("div", { class: "page-header" }, [
      el("div", {}, [
        el("div", { class: "page-title" }, "Clients"),
        el(
          "div",
          { class: "page-subtitle" },
          "Every customer you run work for. Click one to open its command center.",
        ),
      ]),
      el("div", {}, [
        // The legacy manage view still owns account assignment and work tagging;
        // link it rather than stranding it (an unreachable route is a deleted
        // feature that still ships).
        el(
          "button",
          { class: "btn btn-ghost", onclick: () => navigate("clients/manage") },
          "Assign accounts & tag work",
        ),
        el(
          "button",
          {
            class: "btn btn-primary",
            style: "margin-left:8px;",
            onclick: () => _crNewClientModal(renderClientsRoster),
          },
          "+ New client",
        ),
      ]),
    ]),
  );

  if (!clients.length) {
    main.appendChild(
      el("div", { class: "empty-state" }, [
        el("div", { class: "empty-state-icon" }, icon("users")),
        el("div", { class: "empty-state-title" }, "No clients yet"),
        el(
          "div",
          { class: "empty-state-hint" },
          "A client is a customer you run campaigns for. Create one, assign the accounts it uses, then tag its campaigns so its report and console fill in.",
        ),
      ]),
    );
    setContentNode(main);
    return;
  }

  const sortState = dashboardState.sort.clientsRoster || { key: "activeAutomations", dir: "desc" };
  dashboardState.sort.clientsRoster = sortState;

  const columns = [
    { key: "name", label: "Client" },
    { key: "activeAutomations", label: "Running" },
    { key: "pipelineRows", label: "Pipeline" },
    { key: "accountCount", label: "Accounts" },
    { key: "lastActionAt", label: "Last action" },
    { key: null, label: "Next action" },
    { key: null, label: "" },
  ];

  const table = el("table", { class: "table" });
  table.appendChild(
    makeSortableHeader(columns, sortState, (key) => {
      sortState.dir = sortState.key === key && sortState.dir === "desc" ? "asc" : "desc";
      sortState.key = key;
      renderClientsRoster();
    }),
  );

  const tbody = el("tbody", {});
  for (const c of sortRows(clients, sortState.key, sortState.dir)) {
    const na = _crNextAction(c);
    tbody.appendChild(
      el("tr", { style: "cursor:pointer;", onclick: () => navigate(`clients/${c.id}`) }, [
        el("td", {}, [
          _crStateDot(c.state),
          el("span", { style: "font-weight:600;" }, c.name),
          c.archived ? el("span", { class: "badge", style: "margin-left:6px;" }, "archived") : null,
        ]),
        el("td", {}, String(c.activeAutomations ?? 0)),
        // Shown separately and labelled: pipeline rows are per-contact outreach,
        // not automations. Summing them into "running" overstated the number.
        el("td", { class: "muted" }, String(c.pipelineRows ?? 0)),
        el("td", {}, String(c.accountCount ?? 0)),
        el("td", { class: "muted" }, _crAgo(c.lastActionAt)),
        el("td", { style: `color:${_crToneColor(na.tone)};` }, na.text),
        el("td", { style: "text-align:right;white-space:nowrap;" }, [
          el(
            "button",
            {
              class: "btn btn-ghost",
              title: "Pause every automation for this client",
              onclick: (e) => {
                e.stopPropagation();
                _crPauseAll(c, renderClientsRoster);
              },
            },
            "Pause all",
          ),
          el(
            "button",
            {
              class: "btn btn-ghost",
              style: "margin-left:6px;",
              onclick: (e) => {
                e.stopPropagation();
                navigate(`client-report/${c.id}`);
              },
            },
            "Report",
          ),
        ]),
      ]),
    );
  }
  table.appendChild(tbody);
  main.appendChild(el("div", { class: "card" }, [table]));
  setContentNode(main);
}
