// SOLARA SDR — view components.
// Adapted from the Stitch handoff. All data flows through props from real
// /api/* endpoints; no mock state. The visual structure mirrors the design
// pixel-for-pixel (see static/styles.css).

const fmt = {
  num: (n) => (n == null ? "—" : Number(n).toLocaleString()),
  pct: (n, d = 1) => (n == null ? "—" : `${Number(n).toFixed(d)}%`),
  // epoch seconds → relative ("12s ago", "3m ago", "Mar 14")
  rel: (ts) => {
    if (!ts) return "—";
    const now = Math.floor(Date.now() / 1000);
    const d = now - Number(ts);
    if (d < 0) return "now";
    if (d < 60) return `${d}s ago`;
    if (d < 3600) return `${Math.floor(d / 60)}m ago`;
    if (d < 86400) return `${Math.floor(d / 3600)}h ago`;
    if (d < 86400 * 7) return `${Math.floor(d / 86400)}d ago`;
    const dt = new Date(ts * 1000);
    return dt.toLocaleDateString("en-US", { month: "short", day: "numeric" });
  },
  shortName: (n) => {
    if (!n) return "??";
    const parts = String(n).split(/[\s,@.]+/).filter(Boolean);
    return parts.slice(0, 2).map((s) => s[0].toUpperCase()).join("") || "??";
  },
};

// =================================================================
// OVERVIEW
// =================================================================
function OverviewView({ data, onNav, onOpenAside }) {
  const t = data.totals || {};
  const sparkData = data.sparkline?.buckets || [];
  return (
    <>
      <div className="main-header">
        <div>
          <h1 className="main-title">Pipeline overview</h1>
          <div className="main-sub">
            <span style={{ color: "var(--primary)", fontWeight: 500 }}>
              {data.campaigns.filter((c) => c.enabled).length} campaigns enabled.
            </span>{" "}
            {fmt.num(t.leads_inflight)} leads in flight · agent engine{" "}
            {data.engine_status || "scaffold"}.
          </div>
        </div>
        <div className="toolbar">
          <button className="btn ghost" onClick={() => window.location.reload()}>
            <Icon name="refresh" size={14} /> Refresh
          </button>
          <button className="btn aside-toggle" onClick={onOpenAside}>
            <Icon name="bolt" size={14} /> Activity
          </button>
          <button className="btn primary" onClick={() => onNav("campaigns")}>
            <Icon name="plus" size={14} /> Campaigns
          </button>
        </div>
      </div>

      <div className="bento">
        {/* KPI row */}
        <div className="card span-3">
          <div className="card-head">
            <span>Leads</span>
            <span className="meta">all-time</span>
          </div>
          <div className="kpi">
            <div className="kpi-value">{fmt.num(t.leads_total)}</div>
          </div>
          <div className="kpi-sub">
            {fmt.num(t.suppressions || 0)} suppressed
          </div>
        </div>

        <div className="card span-3">
          <div className="card-head">
            <span>Sent · 12h</span>
            <span className="meta">live</span>
          </div>
          <div className="kpi">
            <div className="kpi-value">
              {fmt.num(sparkData.reduce((a, b) => a + b, 0))}
            </div>
          </div>
          <div className="kpi-sub">
            {fmt.num(t.msgs_sent || 0)} all-time outbound
          </div>
          {sparkData.length > 0 && (
            <div style={{ marginTop: 10 }}>
              <Sparkline data={sparkData} />
            </div>
          )}
        </div>

        <div className="card span-3">
          <div className="card-head">
            <span>Replies</span>
            <span className="meta">{fmt.pct(t.reply_rate || 0)} reply rate</span>
          </div>
          <div className="kpi">
            <div className="kpi-value">{fmt.num(t.msgs_received || 0)}</div>
          </div>
          <div className="kpi-sub">{fmt.num(t.leads_replied || 0)} leads replied</div>
        </div>

        <div className="card tint span-3">
          <div className="card-head">
            <span style={{ color: "var(--primary)" }}>Open escalations</span>
            <span className="meta">needs review</span>
          </div>
          <div className="kpi">
            <div className="kpi-value">{data.open_escalations}</div>
          </div>
          <div className="kpi-sub">
            {data.open_escalations > 0 ? "Awaiting human" : "All clear"}
          </div>
          <button
            className="btn"
            style={{ marginTop: 12, width: "100%" }}
            onClick={() => onNav("inbox")}
          >
            Review queue <Icon name="arrow_right" size={12} />
          </button>
        </div>

        {/* Funnel */}
        <div className="card span-7">
          <div className="card-head">
            <span>Pipeline · all time</span>
            <div className="row-flex" style={{ gap: 12 }}>
              <span className="meta">{fmt.num(t.leads_total)} leads</span>
              <span className="meta">→</span>
              <span className="meta" style={{ color: "var(--primary)" }}>
                {fmt.num(t.leads_converted || 0)} won
              </span>
            </div>
          </div>
          {data.funnel && data.funnel.length > 0 ? (
            <Funnel rows={data.funnel} />
          ) : (
            <div className="muted" style={{ padding: 24 }}>
              Funnel data unavailable.
            </div>
          )}
        </div>

        {/* Agent / engine status */}
        <div className="card span-5">
          <div className="card-head">
            <span>Agent status</span>
            <span className="live">
              <span className="dot" />
              {data.engine_status === "operational" ? "OPERATING" : "SCAFFOLD"}
            </span>
          </div>
          <div
            style={{
              display: "grid",
              gridTemplateColumns: "1fr 1fr",
              gap: 14,
              marginTop: 6,
            }}
          >
            <div>
              <div className="muted" style={kpiLabel}>Mailboxes</div>
              <div style={kpiData}>
                {data.campaigns.length} configured · 0 polling
              </div>
              <div style={{ ...kpiSub, color: "hsl(38 70% 35%)" }}>
                ● IMAP listener pending (Day 2)
              </div>
            </div>
            <div>
              <div className="muted" style={kpiLabel}>LLM engine</div>
              <div style={kpiData}>not yet wired</div>
              <div style={kpiSub}>scheduler + sender Day 2</div>
            </div>
            <div>
              <div className="muted" style={kpiLabel}>Database</div>
              <div style={kpiData}>SQLite · {fmt.num(t.leads_total)} leads</div>
              <div style={{ ...kpiSub, color: "hsl(158 70% 30%)" }}>● healthy</div>
            </div>
            <div>
              <div className="muted" style={kpiLabel}>Suppressions</div>
              <div style={kpiData}>{fmt.num(t.suppressions || 0)} addresses</div>
              <div style={kpiSub}>
                bounce-webhook → suppression list active
              </div>
            </div>
          </div>
        </div>

        {/* Top conversations */}
        <div className="card flush span-7">
          <div className="card-head" style={{ padding: "14px 16px 8px" }}>
            <span>Top conversations</span>
            <button
              className="btn ghost"
              style={{ height: 24, padding: "0 8px", fontSize: 11 }}
              onClick={() => onNav("inbox")}
            >
              Open inbox <Icon name="arrow_right" size={11} />
            </button>
          </div>
          {data.threads && data.threads.length > 0 ? (
            <div style={{ overflowX: "auto" }}>
              <table className="tbl">
                <thead>
                  <tr>
                    <th>Lead</th>
                    <th>Campaign</th>
                    <th>Subject</th>
                    <th>Status</th>
                    <th>When</th>
                  </tr>
                </thead>
                <tbody>
                  {data.threads.slice(0, 6).map((th) => {
                    const c = data.campaigns.find((c) => c.id === th.campaign_id) || {};
                    return (
                      <tr key={th.id} onClick={() => onNav("inbox", th.id)}>
                        <td>
                          <div style={{ fontWeight: 600 }}>{th.lead.name}</div>
                          <div style={{ fontSize: 11, color: "var(--fg-muted)" }}>
                            {th.lead.company || th.lead.email}
                          </div>
                        </td>
                        <td>
                          <span className="row-flex" style={{ gap: 6 }}>
                            <span
                              style={{
                                width: 6,
                                height: 6,
                                borderRadius: 2,
                                background: c.color || "var(--fg-muted)",
                              }}
                            />
                            <span style={{ fontSize: 12 }}>
                              {c.display_name || th.campaign_id}
                            </span>
                          </span>
                        </td>
                        <td
                          style={{
                            maxWidth: 240,
                            overflow: "hidden",
                            textOverflow: "ellipsis",
                            whiteSpace: "nowrap",
                          }}
                        >
                          {th.subject}
                        </td>
                        <td>
                          <Status kind={th.status || "queued"} />
                        </td>
                        <td className="mono muted" style={{ fontSize: 11 }}>
                          {fmt.rel(th.ts)}
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          ) : (
            <EmptyState
              icon="inbox"
              title="No conversations yet"
              text="Once messages start flowing, they'll show up here."
            />
          )}
        </div>

        {/* Campaign quick cards */}
        <div className="card span-5">
          <div className="card-head">
            <span>Campaigns</span>
            <button
              className="btn ghost"
              style={{ height: 24, padding: "0 8px", fontSize: 11 }}
              onClick={() => onNav("campaigns")}
            >
              Manage <Icon name="arrow_right" size={11} />
            </button>
          </div>
          <div className="col-flex" style={{ gap: 10 }}>
            {data.campaigns.slice(0, 6).map((c) => {
              const pc = data.per_campaign?.[c.id] || {};
              const total = pc.leads_total || 0;
              const active = pc.leads_inflight || 0;
              const pct = total > 0 ? active / total : 0;
              const replyRate =
                pc.leads_total > 0
                  ? ((pc.leads_replied || 0) / pc.leads_total) * 100
                  : 0;
              return (
                <div
                  key={c.id}
                  onClick={() => onNav("campaigns", c.id)}
                  style={{
                    padding: "10px 12px",
                    border: "1px solid var(--border-soft)",
                    borderRadius: 10,
                    cursor: "pointer",
                    transition: "all 150ms var(--ease)",
                  }}
                >
                  <div
                    className="row-flex"
                    style={{ justifyContent: "space-between", marginBottom: 6 }}
                  >
                    <div className="row-flex" style={{ gap: 8, minWidth: 0 }}>
                      <span
                        style={{
                          width: 8,
                          height: 8,
                          borderRadius: 2,
                          background: c.color,
                          flexShrink: 0,
                        }}
                      />
                      <span style={{ fontWeight: 600, fontSize: 13 }}>
                        {c.display_name}
                      </span>
                      <span
                        className="muted"
                        style={{
                          fontSize: 11,
                          overflow: "hidden",
                          textOverflow: "ellipsis",
                          whiteSpace: "nowrap",
                        }}
                      >
                        · {c.persona_name}
                      </span>
                    </div>
                    {c.enabled ? null : (
                      <span className="muted" style={{ fontSize: 10 }}>paused</span>
                    )}
                  </div>
                  <div
                    style={{
                      height: 3,
                      borderRadius: 999,
                      background: "var(--border-soft)",
                      overflow: "hidden",
                    }}
                  >
                    <div
                      style={{
                        height: "100%",
                        width: `${pct * 100}%`,
                        background: `linear-gradient(90deg, ${c.color}80, ${c.color})`,
                      }}
                    />
                  </div>
                  <div
                    className="row-flex"
                    style={{
                      justifyContent: "space-between",
                      marginTop: 6,
                      fontSize: 11,
                      fontFamily: "var(--font-mono)",
                      color: "var(--fg-muted)",
                    }}
                  >
                    <span>
                      {fmt.num(active)} / {fmt.num(total)} active
                    </span>
                    <span>{fmt.pct(replyRate)} reply</span>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* Escalation queue */}
        <div className="card span-12">
          <div className="card-head">
            <span>Escalation queue · awaiting human</span>
            <span className="meta">
              {data.escalations_queue?.length || 0} open
            </span>
          </div>
          {data.escalations_queue && data.escalations_queue.length > 0 ? (
            <div style={{ overflowX: "auto" }}>
              <table className="tbl">
                <thead>
                  <tr>
                    <th style={{ width: 24 }}></th>
                    <th>Lead</th>
                    <th>Reason</th>
                    <th>Campaign</th>
                    <th>Age</th>
                    <th style={{ textAlign: "right" }}>Actions</th>
                  </tr>
                </thead>
                <tbody>
                  {data.escalations_queue.map((e) => {
                    const c = data.campaigns.find((c) => c.id === e.campaign_id) || {};
                    return (
                      <tr key={e.id}>
                        <td>
                          <Icon name="flag" size={14} />
                        </td>
                        <td>
                          <div style={{ fontWeight: 600 }}>{e.lead}</div>
                          <div className="muted" style={{ fontSize: 11 }}>
                            {e.company || ""}
                          </div>
                        </td>
                        <td>{e.reason}</td>
                        <td>
                          <span className="row-flex" style={{ gap: 6 }}>
                            <span
                              style={{
                                width: 6,
                                height: 6,
                                borderRadius: 2,
                                background: c.color || "var(--fg-muted)",
                              }}
                            />
                            {c.display_name || e.campaign_id}
                          </span>
                        </td>
                        <td className="mono" style={{ fontSize: 11 }}>
                          {fmt.rel(e.created_at)}
                        </td>
                        <td style={{ textAlign: "right" }}>
                          <button
                            className="btn"
                            style={{ height: 24, padding: "0 8px", fontSize: 11 }}
                            onClick={() => onNav("inbox", `l${e.lead_id}`)}
                          >
                            Open
                          </button>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          ) : (
            <EmptyState
              icon="check"
              title="Inbox zero"
              text="No escalations need a human right now."
            />
          )}
        </div>
      </div>
    </>
  );
}

const kpiLabel = {
  fontSize: 11,
  textTransform: "uppercase",
  letterSpacing: "0.06em",
  fontWeight: 600,
  marginBottom: 6,
};
const kpiData = { fontFamily: "var(--font-mono)", fontSize: 12 };
const kpiSub = {
  fontFamily: "var(--font-mono)",
  fontSize: 10.5,
  color: "var(--fg-muted)",
  marginTop: 2,
};

// =================================================================
// INBOX
// =================================================================
function InboxView({ data, threadId, setThreadId, onOpenRail }) {
  const [search, setSearch] = useState("");
  const [filter, setFilter] = useState("all"); // all | unread
  const [pane, setPane] = useState("list"); // mobile single-pane router
  const [thread, setThread] = useState(null);
  const [loading, setLoading] = useState(false);

  const threads = useMemo(() => {
    let list = data.threads || [];
    if (filter === "unread") list = list.filter((t) => t.unread);
    if (search) {
      const s = search.toLowerCase();
      list = list.filter((t) =>
        ((t.lead.name || "") +
          (t.lead.company || "") +
          (t.subject || "") +
          (t.snippet || "")
        )
          .toLowerCase()
          .includes(s)
      );
    }
    return list;
  }, [data.threads, filter, search]);

  // Auto-select first thread once data is available.
  useEffect(() => {
    if (!threadId && threads.length > 0) {
      setThreadId(threads[0].id);
    }
  }, [threadId, threads, setThreadId]);

  // Load thread detail.
  useEffect(() => {
    if (!threadId) {
      setThread(null);
      return;
    }
    const leadId = threadId.replace(/^l/, "");
    setLoading(true);
    fetch(`/api/threads/${leadId}`)
      .then((r) => (r.ok ? r.json() : null))
      .then((data) => {
        setThread(data);
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, [threadId]);

  const t = thread?.lead;
  const c = data.campaigns.find((cc) => cc.id === t?.campaign_id) || {};

  return (
    <>
      <div className="main-header">
        <div>
          <h1 className="main-title">Inbox</h1>
          <div className="main-sub">
            {threads.length} conversation{threads.length === 1 ? "" : "s"} ·{" "}
            {data.open_escalations} need attention
          </div>
        </div>
        <div className="toolbar">
          <div className="seg">
            <button
              className={filter === "all" ? "on" : ""}
              onClick={() => setFilter("all")}
            >
              All
            </button>
            <button
              className={filter === "unread" ? "on" : ""}
              onClick={() => setFilter("unread")}
            >
              Unread
            </button>
          </div>
        </div>
      </div>

      <div className="inbox" data-pane={pane}>
        {/* Thread list */}
        <div className="thread-list">
          <div className="thread-search">
            <Icon name="search" size={14} className="muted" />
            <input
              placeholder="Search conversations…"
              value={search}
              onChange={(e) => setSearch(e.target.value)}
            />
          </div>
          <div className="thread-list-scroll">
            {threads.length === 0 ? (
              <EmptyState
                icon="inbox"
                title="No conversations"
                text="Threads appear here once messages send or replies arrive."
              />
            ) : (
              threads.map((th) => {
                const tc = data.campaigns.find((cc) => cc.id === th.campaign_id) || {};
                return (
                  <div
                    key={th.id}
                    className={`thread-item ${
                      th.id === threadId ? "active" : ""
                    } ${th.unread ? "unread" : ""}`}
                    onClick={() => {
                      setThreadId(th.id);
                      setPane("view");
                    }}
                  >
                    <div className="top">
                      <span className="name">{th.lead.name}</span>
                      <span className="when">{fmt.rel(th.ts)}</span>
                    </div>
                    <div className="subj">{th.subject}</div>
                    <div className="snippet">{th.snippet || ""}</div>
                    <div className="row2">
                      <span
                        style={{
                          display: "inline-flex",
                          alignItems: "center",
                          gap: 5,
                          fontSize: 10.5,
                          fontFamily: "var(--font-mono)",
                          color: "var(--fg-muted)",
                        }}
                      >
                        <span
                          style={{
                            width: 6,
                            height: 6,
                            borderRadius: 2,
                            background: tc.color || "var(--fg-muted)",
                          }}
                        />
                        {tc.display_name || th.campaign_id}
                      </span>
                      <Status kind={th.status || "queued"} />
                    </div>
                  </div>
                );
              })
            )}
          </div>
        </div>

        {/* Thread view */}
        <div className="thread-view">
          {thread && t ? (
            <>
              <div className="thread-head">
                <button
                  className="iconbtn inbox-back"
                  onClick={() => setPane("list")}
                  title="Back"
                >
                  <Icon name="arrow_left" size={14} />
                </button>
                <div
                  className="avatar"
                  style={{
                    width: 36,
                    height: 36,
                    fontSize: 13,
                    background: `linear-gradient(135deg, ${
                      c.color || "#23A06A"
                    }, hsl(218 60% 55%))`,
                  }}
                >
                  {fmt.shortName(t.name)}
                </div>
                <div className="who">
                  <h3>{t.name}</h3>
                  <div className="meta-row">
                    {t.title && <span>{t.title}</span>}
                    {t.title && t.company && (
                      <span style={{ color: "hsl(220 12% 88%)" }}>·</span>
                    )}
                    {t.company && (
                      <span style={{ fontWeight: 500 }}>{t.company}</span>
                    )}
                    {t.email && (
                      <>
                        <span style={{ color: "hsl(220 12% 88%)" }}>·</span>
                        <span className="mono" style={{ fontSize: 11 }}>
                          {t.email}
                        </span>
                      </>
                    )}
                    {t.linkedin && (
                      <>
                        <span style={{ color: "hsl(220 12% 88%)" }}>·</span>
                        <a href={t.linkedin} target="_blank" rel="noreferrer">
                          linkedin
                        </a>
                      </>
                    )}
                    <Status kind={t.status || "queued"} />
                  </div>
                </div>
              </div>

              <div className="thread-body">
                {thread.messages && thread.messages.length > 0 ? (
                  thread.messages.map((m) => (
                    <div key={m.id} className={`msg ${m.direction === "outbound" ? "out" : ""}`}>
                      <div className="msg-head">
                        <div>
                          <span className="from">
                            {m.direction === "outbound" ? c.persona_name : t.name}
                          </span>
                          <span className="from-meta">
                            {m.direction === "outbound" ? m.from_addr : m.from_addr}
                          </span>
                        </div>
                        <div className="when">
                          {fmt.rel(m.sent_at || m.received_at || m.created_at)}
                        </div>
                      </div>
                      <div className="msg-body">{m.body_text || "(no body)"}</div>
                    </div>
                  ))
                ) : (
                  <EmptyState
                    icon="mail"
                    title="No messages yet"
                    text="The sender + IMAP listener arrive in Day 2 — once active, all sends and replies for this lead will thread here."
                  />
                )}
              </div>

              <div className="draft-panel">
                <div className="draft-head">
                  <div className="draft-label">
                    <Icon name="sparkle" size={12} />
                    AI draft
                  </div>
                  <div
                    className="muted"
                    style={{
                      marginLeft: "auto",
                      fontSize: 11,
                      fontFamily: "var(--font-mono)",
                    }}
                  >
                    pending Day-2 LLM engine
                  </div>
                </div>
                <div
                  className="draft-body"
                  style={{ color: "var(--fg-muted)", fontSize: 12.5 }}
                >
                  Drafts will appear here once the LLM engine ships. The persona
                  ({c.persona_name}) and ICP brief from{" "}
                  <strong>{c.display_name}</strong> are already wired in.
                </div>
              </div>
            </>
          ) : loading ? (
            <div
              style={{
                padding: 40,
                color: "var(--fg-muted)",
                fontSize: 13,
                textAlign: "center",
              }}
            >
              Loading thread…
            </div>
          ) : (
            <EmptyState
              icon="inbox"
              title="Select a conversation"
              text="Pick a thread from the list to see the full history."
            />
          )}
        </div>
      </div>
    </>
  );
}

// =================================================================
// PIPELINE
// =================================================================
function PipelineView({ data, onNav }) {
  const [pipeline, setPipeline] = useState(null);
  const [campFilter, setCampFilter] = useState("all");

  useEffect(() => {
    const url =
      campFilter === "all"
        ? "/api/pipeline"
        : `/api/pipeline?campaign_id=${encodeURIComponent(campFilter)}`;
    fetch(url)
      .then((r) => r.json())
      .then(setPipeline);
  }, [campFilter]);

  if (!pipeline) {
    return (
      <div style={{ padding: 40, color: "var(--fg-muted)", textAlign: "center" }}>
        Loading pipeline…
      </div>
    );
  }

  const total = Object.values(pipeline.by_stage).reduce(
    (a, arr) => a + arr.length,
    0
  );

  return (
    <>
      <div className="main-header">
        <div>
          <h1 className="main-title">Pipeline</h1>
          <div className="main-sub">
            {total} lead{total === 1 ? "" : "s"} in motion · stage updates from
            agent activity
          </div>
        </div>
        <div className="toolbar">
          <div className="seg" style={{ overflowX: "auto" }}>
            <button
              className={campFilter === "all" ? "on" : ""}
              onClick={() => setCampFilter("all")}
            >
              All
            </button>
            {data.campaigns.map((c) => (
              <button
                key={c.id}
                className={campFilter === c.id ? "on" : ""}
                onClick={() => setCampFilter(c.id)}
              >
                <span
                  style={{
                    width: 6,
                    height: 6,
                    borderRadius: 2,
                    background: c.color,
                    display: "inline-block",
                    marginRight: 6,
                  }}
                />
                {c.display_name}
              </button>
            ))}
          </div>
        </div>
      </div>

      <div className="pipeline">
        {pipeline.stages.map((stage) => {
          const items = pipeline.by_stage[stage.id] || [];
          return (
            <div className="col" key={stage.id}>
              <div className="col-head">
                <div className="name">
                  <span className="dot" style={{ background: stage.color }} />
                  {stage.name}
                </div>
                <div className="count">{items.length}</div>
              </div>
              <div className="col-body">
                {items.length === 0 ? (
                  <div
                    style={{
                      padding: "24px 12px",
                      textAlign: "center",
                      color: "var(--fg-muted)",
                      fontSize: 12,
                    }}
                  >
                    Empty
                  </div>
                ) : (
                  items.map((l) => {
                    const c = data.campaigns.find((c) => c.id === l.campaign_id) || {};
                    return (
                      <div
                        className="lead-card"
                        key={l.id}
                        onClick={() => onNav("inbox", l.id)}
                      >
                        <div
                          className="row-flex"
                          style={{ justifyContent: "space-between" }}
                        >
                          <div className="name">{l.name}</div>
                        </div>
                        <div className="role">
                          {l.title || ""}
                          {l.title && l.company ? " · " : ""}
                          {l.company || ""}
                        </div>
                        <div className="row">
                          <span
                            style={{
                              display: "inline-flex",
                              alignItems: "center",
                              gap: 4,
                            }}
                          >
                            <span
                              style={{
                                width: 6,
                                height: 6,
                                borderRadius: 2,
                                background: c.color || "var(--fg-muted)",
                              }}
                            />
                            {c.display_name || l.campaign_id}
                          </span>
                          <span
                            style={{
                              maxWidth: 110,
                              overflow: "hidden",
                              textOverflow: "ellipsis",
                              whiteSpace: "nowrap",
                            }}
                          >
                            {l.email}
                          </span>
                        </div>
                      </div>
                    );
                  })
                )}
              </div>
            </div>
          );
        })}
      </div>
    </>
  );
}

// =================================================================
// CAMPAIGNS
// =================================================================
function CampaignsView({ data, onUpload }) {
  return (
    <>
      <div className="main-header">
        <div>
          <h1 className="main-title">Campaigns</h1>
          <div className="main-sub">
            {data.campaigns.filter((c) => c.enabled).length} enabled · drop a
            CSV onto any card to add leads
          </div>
        </div>
      </div>

      <div className="camp-grid">
        {data.campaigns.map((c) => {
          const pc = data.per_campaign?.[c.id] || {};
          const total = pc.leads_total || 0;
          const inflight = pc.leads_inflight || 0;
          const replied = pc.leads_replied || 0;
          const converted = pc.leads_converted || 0;
          const replyRate = total > 0 ? (replied / total) * 100 : 0;
          const pct = total > 0 ? inflight / total : 0;
          return (
            <CampaignCard
              key={c.id}
              campaign={c}
              total={total}
              inflight={inflight}
              replied={replied}
              converted={converted}
              replyRate={replyRate}
              pct={pct}
              onUpload={onUpload}
            />
          );
        })}
      </div>
    </>
  );
}

function CampaignCard({
  campaign: c,
  total,
  inflight,
  replied,
  converted,
  replyRate,
  pct,
  onUpload,
}) {
  const [drag, setDrag] = useState(false);
  const [result, setResult] = useState(null); // {kind, msg}
  const inputRef = useRef(null);

  const upload = (file) => {
    if (!file) return;
    setResult({ kind: "work", msg: `Uploading ${file.name}…` });
    const fd = new FormData();
    fd.append("file", file);
    fetch(`/api/campaigns/${encodeURIComponent(c.id)}/leads/csv`, {
      method: "POST",
      body: fd,
    })
      .then((r) => {
        if (!r.ok) throw new Error("HTTP " + r.status);
        return r.json();
      })
      .then((d) => {
        setResult({
          kind: "ok",
          msg: `Imported ${d.inserted} · ${d.skipped} skipped`,
        });
        if (onUpload) setTimeout(onUpload, 600);
      })
      .catch((e) => setResult({ kind: "err", msg: "Failed: " + e.message }));
  };

  return (
    <div
      className={`camp ${drag ? "dragging" : ""}`}
      onDragEnter={(e) => {
        e.preventDefault();
        setDrag(true);
      }}
      onDragOver={(e) => {
        e.preventDefault();
        setDrag(true);
      }}
      onDragLeave={() => setDrag(false)}
      onDrop={(e) => {
        e.preventDefault();
        setDrag(false);
        const f = e.dataTransfer?.files?.[0];
        if (f) upload(f);
      }}
    >
      <div
        className="accent"
        style={{ background: `linear-gradient(90deg, ${c.color}, ${c.color}80)` }}
      />
      <div
        className="row-flex"
        style={{ justifyContent: "space-between", marginBottom: 10 }}
      >
        <div style={{ minWidth: 0 }}>
          <h4>{c.display_name}</h4>
          <div className="from">
            {c.persona_name} · {c.sender_email}
          </div>
        </div>
        <span
          className={`badge ${c.enabled ? "on" : "off"}`}
          style={{
            padding: "3px 8px",
            borderRadius: 999,
            fontSize: 11,
            fontWeight: 600,
            background: c.enabled ? "hsl(var(--hsl-primary-soft))" : "hsl(220 12% 95%)",
            color: c.enabled ? "hsl(158 70% 25%)" : "var(--fg-muted)",
          }}
        >
          {c.enabled ? "Enabled" : "Paused"}
        </span>
      </div>
      <div style={{ fontSize: 12, color: "var(--fg-muted)", marginBottom: 10 }}>
        {c.vertical || ""}
      </div>

      <div className="stats">
        <div>
          <div className="stat-v">{fmt.num(total)}</div>
          <div className="stat-k">Leads</div>
        </div>
        <div>
          <div className="stat-v">{fmt.num(inflight)}</div>
          <div className="stat-k">Active</div>
        </div>
        <div>
          <div className="stat-v" style={{ color: "var(--primary)" }}>
            {fmt.pct(replyRate)}
          </div>
          <div className="stat-k">Reply</div>
        </div>
        <div>
          <div className="stat-v">{fmt.num(converted)}</div>
          <div className="stat-k">Won</div>
        </div>
      </div>

      <div className="progress">
        <div
          className="fill"
          style={{
            width: `${pct * 100}%`,
            background: `linear-gradient(90deg, ${c.color}, ${c.color}aa)`,
          }}
        />
      </div>
      <div className="progress-row">
        <span>{Math.round(pct * 100)}% active</span>
        <span>{fmt.num(total - inflight)} remaining</span>
      </div>

      <div
        onClick={() => inputRef.current?.click()}
        style={{
          marginTop: 14,
          padding: 10,
          border: `1.5px dashed ${
            drag ? "var(--primary)" : "var(--border)"
          }`,
          borderRadius: 8,
          textAlign: "center",
          background: drag ? "hsl(var(--hsl-primary) / 0.06)" : "var(--bg)",
          color: drag ? "var(--primary)" : "var(--fg-muted)",
          cursor: "pointer",
          fontSize: 12,
          transition: "all 120ms var(--ease)",
        }}
      >
        <input
          ref={inputRef}
          type="file"
          accept=".csv,text/csv"
          style={{ display: "none" }}
          onChange={(e) => {
            const f = e.target.files?.[0];
            if (f) upload(f);
            e.target.value = "";
          }}
        />
        <strong style={{ color: "var(--fg)" }}>Drop CSV</strong> or click ·
        needs an{" "}
        <code style={{ fontFamily: "var(--font-mono)" }}>email</code> column
      </div>
      {result && (
        <div
          style={{
            marginTop: 8,
            padding: "6px 10px",
            borderRadius: 6,
            fontSize: 11.5,
            background:
              result.kind === "ok"
                ? "hsl(var(--hsl-primary-soft))"
                : result.kind === "err"
                ? "hsl(0 80% 96%)"
                : "hsl(218 90% 96%)",
            color:
              result.kind === "ok"
                ? "hsl(158 70% 25%)"
                : result.kind === "err"
                ? "var(--danger)"
                : "hsl(218 60% 40%)",
          }}
        >
          {result.msg}
        </div>
      )}
    </div>
  );
}

// =================================================================
// EMPTY STATE
// =================================================================
function EmptyState({ icon, title, text }) {
  return (
    <div
      style={{
        padding: "44px 24px",
        textAlign: "center",
        color: "var(--fg-muted)",
      }}
    >
      <div
        style={{
          width: 44,
          height: 44,
          borderRadius: 12,
          background: "hsl(var(--hsl-primary) / 0.08)",
          color: "var(--primary)",
          display: "grid",
          placeItems: "center",
          margin: "0 auto 12px",
        }}
      >
        <Icon name={icon} size={20} />
      </div>
      <div
        style={{ color: "var(--fg)", fontWeight: 600, fontSize: 14 }}
      >
        {title}
      </div>
      <div style={{ fontSize: 12.5, marginTop: 4, maxWidth: 340, margin: "4px auto 0" }}>
        {text}
      </div>
    </div>
  );
}

Object.assign(window, {
  OverviewView,
  InboxView,
  PipelineView,
  CampaignsView,
  EmptyState,
  fmt,
});
