// ============================================================================
// IMS — Campaign Management
// CampaignsPage: list, create, status filter
// CampaignDetailPage: influencer table, stats, assign returning
// ============================================================================

const CAMP_STATUS_TONE = {
  Active:    "ok",
  Paused:    "warn",
  Completed: "info",
  Archived:  "neutral",
};

function CampaignsPage() {
  const { data, setPage, createCampaign, updateCampaign, notify } = useApp();
  const [createOpen, setCreateOpen] = useState(false);
  const [editTarget, setEditTarget] = useState(null);
  const [statusFilter, setStatusFilter] = useState(null);
  const [search, setSearch] = useState("");
  const [pg, setPg] = useState(1);
  const perPage = 12;

  const campaigns = data.campaigns || [];

  const filtered = useMemo(() => {
    const q = search.toLowerCase();
    return campaigns.filter(c => {
      if (statusFilter && c.status !== statusFilter) return false;
      if (q && !c.name.toLowerCase().includes(q) && !c.brand.toLowerCase().includes(q)) return false;
      return true;
    });
  }, [campaigns, statusFilter, search]);
  const totalPages = Math.max(1, Math.ceil(filtered.length / perPage));
  const paged = filtered.slice((pg - 1) * perPage, pg * perPage);

  // Pre-compute all per-campaign stats in a single O(n) pass — no per-card filtering
  const campaignStats = useMemo(() => {
    const live = {}, done = {};
    (data.collabInProgress || []).forEach(r => {
      if (!r.campaignId) return;
      live[r.campaignId] = (live[r.campaignId] || 0) + 1;
      if (r.stageState === "Complete") done[r.campaignId] = (done[r.campaignId] || 0) + 1;
    });
    return { live, done };
  }, [data.collabInProgress]);

  const { totalActive, totalInfluencers, totalBudget } = useMemo(() => ({
    totalActive: campaigns.filter(c => c.status === "Active").length,
    totalInfluencers: (data.collabInProgress || []).length,
    totalBudget: campaigns.reduce((s, c) => s + (c.budget || 0), 0),
  }), [campaigns, data.collabInProgress]);

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1>Campaigns</h1>
          <div className="subtitle">Brand deals and projects. Assign returning influencers per campaign.</div>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn primary" onClick={() => setCreateOpen(true)}><Ic.plus /> New Campaign</button>
        </div>
      </div>

      <div className="kpi-row">
        <div className="kpi"><div className="label">Total Campaigns</div><div className="value">{campaigns.length}</div><div className="delta">all time</div></div>
        <div className="kpi featured"><div className="label">Active</div><div className="value">{totalActive}</div><div className="delta">running now</div></div>
        <div className="kpi"><div className="label">Active Influencers</div><div className="value">{totalInfluencers}</div><div className="delta">across all campaigns</div></div>
        <div className="kpi"><div className="label">Total Budget</div><div className="value">${totalBudget.toLocaleString()}</div><div className="delta">all campaigns</div></div>
      </div>

      <div className="toolbar">
        {["Active", "Paused", "Completed", "Archived"].map(s => (
          <button key={s} className={`btn sm ${statusFilter === s ? "primary" : ""}`} onClick={() => { setStatusFilter(statusFilter === s ? null : s); setPg(1); }}>
            {s}
          </button>
        ))}
        {statusFilter && <button className="btn sm ghost" onClick={() => { setStatusFilter(null); setPg(1); }}><Ic.x /> Clear</button>}
        <div className="spacer" />
        <div className="search">
          <Ic.search />
          <input placeholder="Search campaign or brand…" value={search} onChange={e => { setSearch(e.target.value); setPg(1); }} />
          {search && <button className="search-clear" onClick={() => setSearch("")}><Ic.x /></button>}
        </div>
      </div>

      {filtered.length === 0 && (
        <div style={{ textAlign: "center", padding: "64px 0", color: "var(--ink-3)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em" }}>
          {search || statusFilter ? "NO RESULTS — try a different filter" : "NO CAMPAIGNS YET — CREATE ONE ABOVE"}
        </div>
      )}

      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(340px, 1fr))", gap: 14, marginTop: 4 }}>
        {paged.map(c => {
          const count = campaignStats.live[c.id] || 0;
          const completed = campaignStats.done[c.id] || 0;
          const pct = count > 0 ? Math.round((completed / count) * 100) : 0;
          return (
            <div key={c.id} className="card clickable" style={{ cursor: "pointer", position: "relative", overflow: "hidden" }}
              onClick={() => setPage({ key: "campaign-detail", campaign: c })}>
              {/* Status ribbon */}
              <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 3, background: c.status === "Active" ? "var(--ok)" : c.status === "Completed" ? "var(--accent)" : c.status === "Paused" ? "var(--alert)" : "var(--line)" }} />
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 10, marginTop: 4 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 600, fontSize: 14, marginBottom: 3 }}>{c.name}</div>
                  <div className="text-mono text-xs text-mute">{c.id} · {c.brand}</div>
                </div>
                <div style={{ display: "flex", alignItems: "center", gap: 6, flexShrink: 0 }}>
                  <Chip tone={CAMP_STATUS_TONE[c.status] || "neutral"} xs>{c.status}</Chip>
                  <button className="btn ghost sm" style={{ padding: "2px 8px", fontSize: 11 }}
                    onClick={e => { e.stopPropagation(); setEditTarget(c); }}>Edit</button>
                </div>
              </div>
              <div className="text-small text-mute" style={{ marginBottom: 14, lineHeight: 1.6, minHeight: 36 }}>{c.description}</div>
              <div style={{ display: "flex", gap: 0, border: "1px solid var(--line)", borderRadius: 4, overflow: "hidden", marginBottom: 12 }}>
                {[["Influencers", count], ["Completed", completed], ["Budget", `$${(c.budget || 0).toLocaleString()}`]].map(([l, v], idx, arr) => (
                  <div key={l} style={{ flex: 1, padding: "6px 12px", borderRight: idx < arr.length - 1 ? "1px solid var(--line)" : "none", textAlign: "center" }}>
                    <div className="text-mono" style={{ fontSize: 15, fontWeight: 600 }}>{v}</div>
                    <div className="text-mono text-xs text-mute" style={{ textTransform: "uppercase", letterSpacing: "0.08em" }}>{l}</div>
                  </div>
                ))}
              </div>
              {count > 0 && (
                <div>
                  <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
                    <span className="text-xs text-mute">All stages done</span>
                    <span className="text-mono text-xs">{pct}%</span>
                  </div>
                  <div style={{ height: 4, background: "var(--line)", borderRadius: 2, overflow: "hidden" }}>
                    <div style={{ height: "100%", width: `${pct}%`, background: "var(--ok)", borderRadius: 2, transition: "width 0.4s ease" }} />
                  </div>
                </div>
              )}
              <div className="text-mono text-xs text-mute" style={{ marginTop: 10 }}>Created {c.createdAt}</div>
            </div>
          );
        })}
      </div>

      {totalPages > 1 && (
        <div style={{ marginTop: 16 }}>
          <TablePagination page={pg} totalPages={totalPages} onPage={setPg} total={filtered.length} perPage={perPage} />
        </div>
      )}

      {createOpen && (
        <CreateCampaignModal
          onClose={() => setCreateOpen(false)}
          onSave={(camp) => {
            createCampaign(camp);
            setCreateOpen(false);
            notify(`Campaign "${camp.name}" created.`);
          }}
        />
      )}
      {editTarget && (
        <EditCampaignModal
          campaign={editTarget}
          onClose={() => setEditTarget(null)}
          onSave={(updates) => {
            updateCampaign(editTarget.id, updates);
            setEditTarget(null);
            notify(`Campaign "${updates.name}" updated.`);
          }}
        />
      )}
    </div>
  );
}

function CampaignDetailPage({ campaign }) {
  const { data, setPage, assignReturningToCampaign, updateCampaign, dropBundle, notify } = useApp();
  const [assignOpen, setAssignOpen] = useState(false);
  const [editOpen, setEditOpen] = useState(false);
  const [tab, setTab] = useState("influencers");
  const [search, setSearch] = useState("");
  const [pg, setPg] = useState(1);
  const [dropTarget, setDropTarget] = useState(null);
  const [bundleSearch, setBundleSearch] = useState("");
  const [bundleListPg, setBundleListPg] = useState(1);
  const BUNDLE_LIST_PER_PAGE = 5;
  const perPage = 10;

  // Keep local campaign state in sync after edits
  const [campData, setCampData] = useState(campaign);
  const liveCamp = (data.campaigns || []).find(c => c.id === campaign.id) || campData;

  const assignments = (data.collabInProgress || []).filter(r => r.campaignId === campaign.id && !r.deletedAt);
  const filtered = search
    ? assignments.filter(r => r.name.toLowerCase().includes(search.toLowerCase()) || r.handle.toLowerCase().includes(search.toLowerCase()))
    : assignments;
  const totalPages = Math.max(1, Math.ceil(filtered.length / perPage));
  const pagedRows = filtered.slice((pg - 1) * perPage, pg * perPage);

  const active    = assignments.filter(r => r.stageState !== "Complete" && r.stageState !== "Dropped").length;
  const completed = assignments.filter(r => r.stageState === "Complete").length;
  const returning = assignments.filter(r => r.isReturning).length;

  // Pending bundles for this campaign (CM inbox, state=pending, matching campaignId)
  const pendingBundles = useMemo(() =>
    (data.cmInbox || []).filter(b => b.campaignId === campaign.id && b.state === "pending"),
  [data.cmInbox, campaign.id]);

  const filteredBundles = useMemo(() => {
    if (!bundleSearch.trim()) return pendingBundles;
    const q = bundleSearch.toLowerCase();
    return pendingBundles.filter(b =>
      b.id.toLowerCase().includes(q) ||
      (b.cmId || "").toLowerCase().includes(q) ||
      (data.collabManagers || []).find(c => c.id === b.cmId)?.name.toLowerCase().includes(q) ||
      (b.influencers || []).some(inf =>
        inf.name.toLowerCase().includes(q) || inf.handle.toLowerCase().includes(q)
      )
    );
  }, [pendingBundles, bundleSearch, data.collabManagers]);

  const bundleTotalPages = Math.max(1, Math.ceil(filteredBundles.length / BUNDLE_LIST_PER_PAGE));
  const pagedBundles = filteredBundles.slice((bundleListPg - 1) * BUNDLE_LIST_PER_PAGE, bundleListPg * BUNDLE_LIST_PER_PAGE);


  return (
    <div className="page">
      <button className="btn ghost sm" onClick={() => setPage({ key: "campaigns" })} style={{ marginBottom: 16 }}>
        <Ic.arrowLeft /> All Campaigns
      </button>

      {/* Header */}
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 20 }}>
        <div>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
            <h1 style={{ margin: 0 }}>{liveCamp.name}</h1>
            <Chip tone={CAMP_STATUS_TONE[liveCamp.status] || "neutral"}>{liveCamp.status}</Chip>
          </div>
          <div className="text-mono text-xs text-mute">{liveCamp.id} · {liveCamp.brand} · Created {liveCamp.createdAt}</div>
          {liveCamp.description && <div className="text-small" style={{ color: "var(--ink-2)", marginTop: 6, maxWidth: 560 }}>{liveCamp.description}</div>}
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn" onClick={() => setEditOpen(true)}>Edit Campaign</button>
          <button className="btn" onClick={() => setAssignOpen(true)}>
            <Ic.arrowRight /> Add Returning Influencer
          </button>
        </div>
      </div>

      {/* KPIs */}
      <div style={{ display: "flex", gap: 0, border: "1px solid var(--line)", borderRadius: 4, overflow: "hidden", marginBottom: 20 }}>
        {[["Total", assignments.length], ["Active", active], ["Completed", completed], ["Returning", returning], ["Budget", `$${(liveCamp.budget || 0).toLocaleString()}`]].map(([l, v], i, a) => (
          <div key={l} style={{ padding: "8px 20px", borderRight: i < a.length - 1 ? "1px solid var(--line)" : "none", textAlign: "center" }}>
            <div className="text-mono" style={{ fontSize: 18, fontWeight: 600 }}>{v}</div>
            <div className="text-mono text-xs text-mute" style={{ textTransform: "uppercase", letterSpacing: "0.08em" }}>{l}</div>
          </div>
        ))}
      </div>

      {/* Tabs */}
      <div className="tabs" style={{ marginBottom: 16 }}>
        {[
          ["influencers", "Influencers", null],
          ["bundles", "Pending Bundles", pendingBundles.length],
        ].map(([k, l, count]) => (
          <div key={k} className={`tab ${tab === k ? "active" : ""}`} onClick={() => { setTab(k); setSearch(""); setPg(1); setBundleSearch(""); setBundleListPg(1); }}>
            {l}
            {count !== null && <span className="count" style={count > 0 ? { background: "var(--alert)", color: "#fff" } : {}}>{count}</span>}
          </div>
        ))}
      </div>

      {tab === "influencers" && (
        <>
          <div className="toolbar" style={{ marginBottom: 14 }}>
            <div className="spacer" />
            <div className="search">
              <Ic.search />
              <input placeholder="Search influencer…" value={search} onChange={e => { setSearch(e.target.value); setPg(1); }} />
              {search && <button className="search-clear" onClick={() => setSearch("")}><Ic.x /></button>}
            </div>
          </div>

          {filtered.length === 0 && (
            <div style={{ textAlign: "center", padding: "48px 0", color: "var(--ink-3)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em" }}>
              NO INFLUENCERS IN THIS CAMPAIGN YET
            </div>
          )}

          {filtered.length > 0 && (
            <div className="table-wrap">
              <table className="ims">
                <thead>
                  <tr>
                    <th>Influencer</th>
                    <th>CM</th>
                    <th>Type</th>
                    <th>Stage Progress</th>
                    <th>State</th>
                  </tr>
                </thead>
                <tbody>
                  {pagedRows.map(r => (
                    <tr key={r.assignmentId} className="clickable" onClick={() => setPage({ key: "profile", influencer: r, from: "campaign-detail", fromCampaign: campaign })}>
                      <td><InfCell name={r.name} handle={r.handle} /></td>
                      <td className="text-small">{r.cmName}</td>
                      <td>
                        {r.isReturning
                          ? <Chip tone="accent" xs>Returning</Chip>
                          : <Chip tone="neutral" xs>First-time</Chip>}
                      </td>
                      <td>
                        {r.stages
                          ? <StageBar stages={r.stages} currentStage={r.currentStage} stageState={r.stageState} />
                          : <span className="text-mute">—</span>}
                      </td>
                      <td>
                        {r.stageState === "Complete"
                          ? <Chip tone="ok" xs>Completed</Chip>
                          : <Chip tone="info" xs>Stage {r.currentStage} · {r.stageState}</Chip>}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
              {totalPages > 1 && (
                <div style={{ padding: "8px 0" }}>
                  <TablePagination page={pg} totalPages={totalPages} onPage={setPg} total={filtered.length} perPage={perPage} />
                </div>
              )}
            </div>
          )}
        </>
      )}


      {/* Pending Bundles tab */}
      {tab === "bundles" && (
        <>
          {/* Search toolbar */}
          <div className="toolbar" style={{ marginBottom: 14 }}>
            <div className="spacer" />
            <div className="search">
              <Ic.search />
              <input placeholder="Search bundle, manager or influencer…" value={bundleSearch}
                onChange={e => { setBundleSearch(e.target.value); setBundleListPg(1); }} />
              {bundleSearch && <button className="search-clear" onClick={() => { setBundleSearch(""); setBundleListPg(1); }}><Ic.x /></button>}
            </div>
          </div>

          {filteredBundles.length === 0 ? (
            <div style={{ textAlign: "center", padding: "48px 0", color: "var(--ink-3)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em" }}>
              {bundleSearch ? "NO BUNDLES MATCH YOUR SEARCH" : "NO PENDING BUNDLES FOR THIS CAMPAIGN"}
            </div>
          ) : (
            <>
              <div style={{ display: "flex", flexDirection: "column", gap: 12, marginBottom: 12 }}>
                {pagedBundles.map(b => {
                  const cm = (data.collabManagers || []).find(c => c.id === b.cmId);
                  return (
                    <BundleInfluencerCard
                      key={b.id}
                      bundle={b}
                      searchQ={bundleSearch}
                      onDrop={setDropTarget}
                      cmLabel={cm ? cm.name : b.cmId}
                    />
                  );
                })}
              </div>
              <TablePagination
                page={bundleListPg}
                totalPages={bundleTotalPages}
                onPage={setBundleListPg}
                total={filteredBundles.length}
                perPage={BUNDLE_LIST_PER_PAGE}
              />
            </>
          )}
        </>
      )}

      {dropTarget && (
        <DropCMBundleModal
          bundle={dropTarget}
          cmName={(data.collabManagers || []).find(c => c.id === dropTarget.cmId)?.name || dropTarget.cmId}
          campaignName={liveCamp.name}
          onConfirm={(reason) => {
            dropBundle(dropTarget.id, "collab", reason);
            notify(`Bundle ${dropTarget.id} dropped — ${dropTarget.influencers?.length || 0} influencer(s) returned to unassigned pool.`);
            setDropTarget(null);
          }}
          onClose={() => setDropTarget(null)}
        />
      )}

      {assignOpen && (
        <AssignReturningModal
          campaign={liveCamp}
          onClose={() => setAssignOpen(false)}
          onAssign={(influencers, cmId) => {
            assignReturningToCampaign(influencers, liveCamp.id, liveCamp.name, cmId);
            setAssignOpen(false);
            notify(`Bundle sent to CM inbox — ${influencers.length} returning influencer${influencers.length > 1 ? "s" : ""} awaiting acceptance for ${liveCamp.name}.`);
          }}
        />
      )}
      {editOpen && (
        <EditCampaignModal
          campaign={liveCamp}
          onClose={() => setEditOpen(false)}
          onSave={(updates) => {
            updateCampaign(liveCamp.id, updates);
            setEditOpen(false);
            notify(`Campaign "${updates.name}" updated.`);
          }}
        />
      )}
    </div>
  );
}

function CreateCampaignModal({ onClose, onSave }) {
  const [name, setName]   = useState("");
  const [brand, setBrand] = useState("");
  const [budget, setBudget] = useState("");
  const [desc, setDesc]   = useState("");
  const [status, setStatus] = useState("Active");

  const valid = name.trim() && brand.trim();

  const save = () => {
    if (!valid) return;
    const now = new Date();
    onSave({
      id: "CAMP-" + (100 + Math.floor(Math.random() * 900)),
      name: name.trim(),
      brand: brand.trim(),
      budget: parseFloat(budget) || 0,
      description: desc.trim(),
      status,
      createdAt: now.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }),
      influencerCount: 0,
    });
  };

  return (
    <Modal open={true} onClose={onClose} title="New Campaign" sub="Create a brand deal or project"
      foot={<>
        <button className="btn" onClick={onClose}>Cancel</button>
        <button className="btn primary" disabled={!valid} onClick={save}>Create Campaign</button>
      </>}>
      <div className="stack-sm">
        <div className="field">
          <label>Campaign Name *</label>
          <input className="input" placeholder="e.g. Nike Summer Drop 2025" value={name} onChange={e => setName(e.target.value)} autoFocus />
        </div>
        <div className="field">
          <label>Brand *</label>
          <input className="input" placeholder="e.g. Nike" value={brand} onChange={e => setBrand(e.target.value)} />
        </div>
        <div className="field">
          <label>Budget (USD)</label>
          <input className="input" type="number" placeholder="e.g. 8000" value={budget} onChange={e => setBudget(e.target.value)} />
        </div>
        <div className="field">
          <label>Status</label>
          <div style={{ display: "flex", gap: 8 }}>
            {["Active", "Paused"].map(s => (
              <div key={s} className={`radio ${status === s ? "checked" : ""}`} onClick={() => setStatus(s)} style={{ flex: 1 }}>
                <div className="dot" />
                <span>{s}</span>
              </div>
            ))}
          </div>
        </div>
        <div className="field">
          <label>Description</label>
          <textarea className="textarea" placeholder="Brief description of this campaign's goals and scope…" value={desc} onChange={e => setDesc(e.target.value)} style={{ minHeight: 80 }} />
        </div>
      </div>
    </Modal>
  );
}

function EditCampaignModal({ campaign, onClose, onSave }) {
  const [name, setName]     = useState(campaign.name || "");
  const [brand, setBrand]   = useState(campaign.brand || "");
  const [budget, setBudget] = useState(campaign.budget != null ? String(campaign.budget) : "");
  const [desc, setDesc]     = useState(campaign.description || "");
  const [status, setStatus] = useState(campaign.status || "Active");

  const valid = name.trim() && brand.trim();

  const save = () => {
    if (!valid) return;
    onSave({
      name: name.trim(),
      brand: brand.trim(),
      budget: parseFloat(budget) || 0,
      description: desc.trim(),
      status,
    });
  };

  return (
    <Modal open={true} onClose={onClose} title="Edit Campaign" sub={`Editing ${campaign.id}`}
      foot={<>
        <button className="btn" onClick={onClose}>Cancel</button>
        <button className="btn primary" disabled={!valid} onClick={save}>Save Changes</button>
      </>}>
      <div className="stack-sm">
        <div className="field">
          <label>Campaign Name *</label>
          <input className="input" value={name} onChange={e => setName(e.target.value)} autoFocus />
        </div>
        <div className="field">
          <label>Brand *</label>
          <input className="input" value={brand} onChange={e => setBrand(e.target.value)} />
        </div>
        <div className="field">
          <label>Budget (USD)</label>
          <input className="input" type="number" value={budget} onChange={e => setBudget(e.target.value)} />
        </div>
        <div className="field">
          <label>Status</label>
          <div style={{ display: "flex", gap: 8 }}>
            {["Active", "Paused", "Completed", "Archived"].map(s => (
              <div key={s} className={`radio ${status === s ? "checked" : ""}`} onClick={() => setStatus(s)} style={{ flex: 1 }}>
                <div className="dot" />
                <span>{s}</span>
              </div>
            ))}
          </div>
        </div>
        <div className="field">
          <label>Description</label>
          <textarea className="textarea" value={desc} onChange={e => setDesc(e.target.value)} style={{ minHeight: 80 }} />
        </div>
      </div>
    </Modal>
  );
}

function AssignReturningModal({ campaign, onClose, onAssign }) {
  const { data } = useApp();
  const [selected, setSelected] = useState(new Set());
  const [manualMode, setManualMode] = useState(false);
  const [bundleFlow, setBundleFlow] = useState(null);
  const [search, setSearch] = useState("");
  const [pg, setPg] = useState(1);
  const perPage = 8;

  const currentIds = useMemo(() =>
    new Set((data.collabInProgress || []).filter(r => r.campaignId === campaign.id).map(r => r.id))
  , [data, campaign.id]);

  const stage1Complete = r => {
    if (!r.hasSignedContract) return false;
    const s1 = (r.stages || []).find(s => s.n === 1);
    return s1?.state === "Complete" || r.currentStage > 1;
  };

  const candidates = useMemo(() => {
    const seen = new Set();
    const out = [];
    const add = r => { if (!seen.has(r.id)) { seen.add(r.id); out.push(r); } };
    (data.collabInProgress || []).filter(r => !r.deletedAt && r.stageState !== "Dropped" && stage1Complete(r) && !currentIds.has(r.id)).forEach(add);
    (data.unassignedLeads || []).filter(r => r.hasSignedContract && !currentIds.has(r.id)).forEach(add);
    return out;
  }, [data, currentIds]);

  const filtered = search
    ? candidates.filter(r => r.name.toLowerCase().includes(search.toLowerCase()) || r.handle.toLowerCase().includes(search.toLowerCase()) || (r.platform || "").toLowerCase().includes(search.toLowerCase()))
    : candidates;

  const totalPages = Math.max(1, Math.ceil(filtered.length / perPage));
  const paged = filtered.slice((pg - 1) * perPage, pg * perPage);

  const toggleSelect = id => {
    const n = new Set(selected);
    n.has(id) ? n.delete(id) : n.add(id);
    setSelected(n);
  };

  const startAuto = () => setBundleFlow({ step: 1, count: candidates.length, specialistId: null, manual: false, campaignId: campaign.id, campaignName: campaign.name, isReturning: true });
  const startManual = () => {
    if (selected.size === 0) return;
    setBundleFlow({ step: 1, count: selected.size, specialistId: null, manual: true, campaignId: campaign.id, campaignName: campaign.name, isReturning: true });
  };
  const cancelManual = () => { setManualMode(false); setSelected(new Set()); };

  if (bundleFlow) {
    return (
      <ReturningBundleFlowModal
        flow={bundleFlow}
        setFlow={setBundleFlow}
        manualCount={selected.size}
        totalAvailable={candidates.length}
        candidates={candidates}
        selectedIds={[...selected]}
        campaign={campaign}
        onClose={() => setBundleFlow(null)}
        onComplete={(summary) => {
          const rows = summary.manual
            ? candidates.filter(r => summary.bundledIds.includes(r.id))
            : candidates.slice(0, summary.count);
          onAssign(rows, summary.specialistId);
          setBundleFlow(null);
        }}
      />
    );
  }

  return (
    <Modal open={true} onClose={onClose}
      title="Add Returning Influencer"
      sub={`Campaign: ${campaign.name} · ${candidates.length} eligible`}
      size="wide"
      foot={<button className="btn" onClick={onClose}>Close</button>}
    >
      {/* Toolbar */}
      <div className="toolbar" style={{ marginBottom: 12 }}>
        {!manualMode ? (
          <>
            <button className="btn primary" disabled={candidates.length === 0} onClick={startAuto}>
              <Ic.plus /> Create Bundle
            </button>
            <button className="btn" disabled={candidates.length === 0} onClick={() => setManualMode(true)}>
              <Ic.filter /> Manual Selection
            </button>
          </>
        ) : null}
        <div className="spacer" />
        <div className="search">
          <Ic.search />
          <input placeholder="Search name, handle, platform…" value={search}
            onChange={e => { setSearch(e.target.value); setPg(1); }} />
          {search && <button className="search-clear" onClick={() => { setSearch(""); setPg(1); }}><Ic.x /></button>}
        </div>
      </div>

      {manualMode && (
        <div className="sticky-widget">
          <span className="count">{selected.size} selected</span>
          <span className="hint">Select returning influencers to bundle together</span>
          <div className="spacer" />
          <button className="btn primary" disabled={selected.size === 0} onClick={startManual}>
            Create Bundle from Selection <Ic.arrowRight />
          </button>
          <button className="x" onClick={cancelManual} aria-label="Cancel"><Ic.x /></button>
        </div>
      )}

      <div className="table-wrap">
        <table className="ims">
          <thead>
            <tr>
              {manualMode && <th style={{ width: 42 }} />}
              <th>Influencer</th>
              <th>Platform</th>
              <th>Followers</th>
              <th>Category</th>
              <th>Previous Campaign</th>
              <th>Stage Status</th>
            </tr>
          </thead>
          <tbody>
            {candidates.length === 0 && (
              <tr><td colSpan={99} style={{ textAlign: "center", padding: "56px 0", color: "var(--ink-3)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em" }}>
                NO ELIGIBLE RETURNING INFLUENCERS — INFLUENCERS MUST COMPLETE STAGE 1 IN ANOTHER CAMPAIGN FIRST
              </td></tr>
            )}
            {candidates.length > 0 && paged.length === 0 && (
              <tr><td colSpan={99} style={{ textAlign: "center", padding: "48px 0", color: "var(--ink-3)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em" }}>
                NO RESULTS — try a different search
              </td></tr>
            )}
            {paged.map(r => (
              <tr key={r.id}
                className={`${manualMode ? "" : "clickable"} ${manualMode && selected.has(r.id) ? "selected" : ""}`}
                onClick={() => { if (manualMode) toggleSelect(r.id); }}>
                {manualMode && (
                  <td>
                    <label className="checkbox" onClick={e => e.stopPropagation()}>
                      <input type="checkbox" checked={selected.has(r.id)} onChange={() => toggleSelect(r.id)} />
                      <span className="box" />
                    </label>
                  </td>
                )}
                <td><InfCell name={r.name} handle={r.handle} /></td>
                <td className="text-small">{r.platform}</td>
                <td className="text-small">{r.followers}</td>
                <td className="text-small">{r.category}</td>
                <td className="text-small">{r.campaignName || <span className="text-mute">—</span>}</td>
                <td>
                  {r.stageState === "Complete"
                    ? <Chip tone="ok" xs>All stages done</Chip>
                    : <Chip tone="info" xs>Stage {r.currentStage} complete</Chip>}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <TablePagination page={pg} totalPages={totalPages} onPage={setPg} total={filtered.length} perPage={perPage} />
    </Modal>
  );
}

// 3-step wizard for returning bundle (Count → CM → Review) — campaign already known
function ReturningBundleFlowModal({ flow, setFlow, manualCount, totalAvailable, candidates, selectedIds, campaign, onClose, onComplete }) {
  const { data } = useApp();
  const maxStep = 3;
  const cms = useMemo(() => (data.collabManagers || []).filter(m => !m.deletedAt), [data.collabManagers]);
  const cm = cms.find(m => m.id === flow.specialistId);
  const bundleId = useMemo(() => "LB-R-" + (750 + Math.floor(Math.random() * 200)), []);

  const [cmSearch, setCmSearch] = useState("");
  const [cmPg, setCmPg] = useState(1);
  const CM_PER_PAGE = 6;

  const filteredCms = useMemo(() => {
    const q = cmSearch.trim().toLowerCase();
    return q ? cms.filter(m => m.name.toLowerCase().includes(q) || m.id.toLowerCase().includes(q)) : cms;
  }, [cms, cmSearch]);

  const cmTotalPages = Math.max(1, Math.ceil(filteredCms.length / CM_PER_PAGE));
  const pagedCms = filteredCms.slice((cmPg - 1) * CM_PER_PAGE, cmPg * CM_PER_PAGE);
  const handleCmSearch = (v) => { setCmSearch(v); setCmPg(1); };

  const selectedRows = flow.manual
    ? candidates.filter(r => selectedIds.includes(r.id))
    : candidates.slice(0, flow.count);

  const next = () => {
    const updates = flow.step === 2 && !flow.bundleId ? { bundleId } : {};
    setFlow({ ...flow, step: flow.step + 1, ...updates });
  };
  const back = () => setFlow({ ...flow, step: flow.step - 1 });

  const stepTitles = ["How many to bundle?", "Select Collaboration Manager", "Review & Create Bundle"];
  const stepTitle = stepTitles[flow.step - 1] || stepTitles[0];

  const canNext =
    flow.step === 1 ? flow.count > 0 :
    flow.step === 2 ? !!flow.specialistId : true;

  const confirm = () => {
    if (flow.step < maxStep) { next(); return; }
    onComplete({ ...flow, bundledIds: selectedRows.map(r => r.id), count: selectedRows.length });
  };

  return (
    <Modal open={true} onClose={onClose}
      title={stepTitle}
      sub={`Step ${flow.step} of ${maxStep} · Returning Bundle · ${campaign.name}`}
      size="wide"
      foot={<>
        <div className="step-dots">
          {Array.from({ length: maxStep }, (_, i) => (
            <span key={i} className={`dot ${flow.step >= i + 1 ? "active" : ""}`} />
          ))}
          <span style={{ marginLeft: 8 }}>Step {flow.step}/{maxStep}</span>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          {flow.step > 1
            ? <button className="btn" onClick={back}><Ic.arrowLeft /> Back</button>
            : <button className="btn" onClick={onClose}>Cancel</button>}
          <button className="btn primary" disabled={!canNext} onClick={confirm}>
            {flow.step === maxStep ? "Create Bundle" : "Continue"} <Ic.arrowRight />
          </button>
        </div>
      </>}
    >

      {/* Step 1: Count */}
      {flow.step === 1 && (
        <>
          {flow.manual ? (
            <>
              <div className="banner info" style={{ marginBottom: 14 }}>
                <Ic.info />
                <div>Manual selection — <strong>{manualCount} hand-picked</strong> returning influencers will be bundled.</div>
              </div>
              <div className="text-mute text-xs" style={{ fontFamily: "var(--font-mono)", textTransform: "uppercase", letterSpacing: "0.1em" }}>
                The system will bundle exactly these {manualCount} influencer{manualCount !== 1 ? "s" : ""} · continue to select a Collaboration Manager
              </div>
            </>
          ) : (
            <>
              <div className="text-mute" style={{ marginBottom: 14 }}>
                You have <strong style={{ color: "var(--ink)" }}>{totalAvailable} returning {totalAvailable === 1 ? "influencer" : "influencers"}</strong> eligible for this campaign. How many do you want to bundle?
              </div>
              <input
                className="input big"
                type="number"
                value={flow.count}
                min="1"
                max={totalAvailable}
                onChange={e => setFlow({ ...flow, count: Math.max(1, Math.min(totalAvailable, +e.target.value || 1)) })}
                autoFocus
              />
              <div className="text-mute text-xs" style={{ marginTop: 10, fontFamily: "var(--font-mono)", textTransform: "uppercase", letterSpacing: "0.1em" }}>
                The system will auto-select {flow.count} influencer{flow.count !== 1 ? "s" : ""} from the top of the list
              </div>
            </>
          )}
        </>
      )}

      {/* Step 2: Select CM */}
      {flow.step === 2 && (
        <>
          <div className="text-mute" style={{ marginBottom: 14 }}>
            <strong style={{ color: "var(--ink)" }}>{flow.manual ? manualCount : flow.count} returning {(flow.manual ? manualCount : flow.count) === 1 ? "influencer" : "influencers"}</strong> will be bundled. Select which Collaboration Manager receives this bundle.
          </div>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
            <span style={{ fontSize: 11, color: "var(--ink-3)", fontFamily: "var(--font-mono)" }}>
              {filteredCms.length === cms.length ? `${cms.length} total` : `${filteredCms.length} of ${cms.length}`}
            </span>
          </div>
          {cms.length > CM_PER_PAGE && (
            <div className="search" style={{ marginBottom: 10 }}>
              <Ic.search />
              <input placeholder="Search manager by name or ID…" value={cmSearch} onChange={e => handleCmSearch(e.target.value)} />
              {cmSearch && <button className="search-clear" onClick={() => handleCmSearch("")}><Ic.x /></button>}
            </div>
          )}
          <div className="stack-sm">
            {pagedCms.length === 0 && <div className="text-mute text-small" style={{ padding: "12px 0" }}>No results for "{cmSearch}".</div>}
            {pagedCms.map(m => (
              <div key={m.id} className={`radio ${flow.specialistId === m.id ? "checked" : ""}`}
                onClick={() => setFlow({ ...flow, specialistId: m.id })}>
                <div className="dot" />
                <div style={{ width: 28, height: 28, background: "var(--bg-3)", borderRadius: 999, display: "grid", placeItems: "center", fontFamily: "var(--font-mono)", fontSize: 10, fontWeight: 600, border: "1px solid var(--line-2)", flexShrink: 0 }}>
                  {m.initials}
                </div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontWeight: 500 }}>{m.name}</div>
                  <div className="text-mono text-xs text-mute">
                    {m.id} · {m.active || 0} active · S1 ${m.stages?.[1]} / S2 ${m.stages?.[2]} / S3 ${m.stages?.[3]} / S4 ${m.stages?.[4]} · {m.commission} / {m.commissionDuration}
                  </div>
                </div>
              </div>
            ))}
          </div>
          {cmTotalPages > 1 && (
            <div style={{ marginTop: 10 }}>
              <TablePagination page={cmPg} totalPages={cmTotalPages} onPage={setCmPg} total={filteredCms.length} perPage={CM_PER_PAGE} />
            </div>
          )}
        </>
      )}

      {/* Step 3: Review */}
      {flow.step === 3 && cm && (
        <>
          <div className="text-mute" style={{ marginBottom: 14 }}>
            Bundle <strong style={{ color: "var(--ink)" }}>{flow.bundleId || bundleId}</strong> will be sent to <strong style={{ color: "var(--ink)" }}>{cm.name}</strong>'s inbox. They must accept before influencers enter the active pipeline.
          </div>
          <div className="card tinted" style={{ marginBottom: 14 }}>
            <div className="kv-list">
              <div className="kv"><div className="k">Bundle ID</div><div className="v text-mono">{flow.bundleId || bundleId}</div></div>
              <div className="kv"><div className="k">Type</div><div className="v"><Chip tone="accent" xs>Returning</Chip></div></div>
              <div className="kv"><div className="k">Influencers</div><div className="v text-mono">{selectedRows.length}</div></div>
              <div className="kv"><div className="k">Campaign</div><div className="v"><Chip tone="info" xs>{campaign.name}</Chip></div></div>
              <div className="kv"><div className="k">Collab Manager</div><div className="v">{cm.name} · {cm.id}</div></div>
              <div className="kv"><div className="k">Stage Payments</div><div className="v text-mono">S1 Skipped · S2 ${cm.stages?.[2]} · S3 ${cm.stages?.[3]} · S4 ${cm.stages?.[4]}</div></div>
              <div className="kv"><div className="k">Commission</div><div className="v">{cm.commission} · {cm.commissionDuration}</div></div>
            </div>
          </div>
          <div className="text-mono text-xs text-mute" style={{ textTransform: "uppercase", letterSpacing: "0.1em", marginBottom: 8 }}>Influencers in this bundle</div>
          <div className="table-wrap" style={{ margin: 0 }}>
            <table className="ims" style={{ fontSize: 12 }}>
              <thead><tr><th>Influencer</th><th>Platform</th><th>Followers</th><th>Previous Campaign</th><th>Status</th></tr></thead>
              <tbody>
                {selectedRows.map(r => (
                  <tr key={r.id}>
                    <td><InfCell name={r.name} handle={r.handle} /></td>
                    <td className="text-small">{r.platform}</td>
                    <td className="text-small">{r.followers}</td>
                    <td className="text-small">{r.campaignName || "—"}</td>
                    <td><Chip tone="ok" xs>Contract signed</Chip></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <div className="banner info" style={{ marginTop: 14 }}>
            <Ic.info />
            <div>Stage 1 pre-completed (contract on file). CM starts at Stage 2. No OS fee generated.</div>
          </div>
        </>
      )}
    </Modal>
  );
}

function DropCMBundleModal({ bundle, cmName, campaignName, onConfirm, onClose }) {
  const infCount = bundle.influencers?.length || 0;
  const [reason, setReason] = React.useState("");
  return (
    <Modal open onClose={onClose} title="Drop Bundle" size="sm">
      <div className="banner warn" style={{ marginBottom: 16 }}>
        <Ic.warn />
        <div>
          <strong>This action cannot be undone.</strong> The manager will be notified and all influencers will return to the unassigned pool.
        </div>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 20 }}>
        {[
          ["Bundle", bundle.id],
          ["Campaign", campaignName],
          ["Manager", cmName],
          ["Influencers", `${infCount} will return to unassigned`],
          ["Assigned", bundle.assignedAt || "—"],
        ].map(([l, v]) => (
          <div key={l} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", borderBottom: "1px solid var(--line)", paddingBottom: 8 }}>
            <span className="text-mono text-xs text-mute" style={{ textTransform: "uppercase", letterSpacing: "0.08em" }}>{l}</span>
            <span style={{ fontSize: 13, fontWeight: 500 }}>{v}</span>
          </div>
        ))}
      </div>
      <div style={{ marginBottom: 20 }}>
        <label style={{ display: "block", fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.08em", color: "var(--ink-3)", marginBottom: 6 }}>
          Reason <span style={{ fontWeight: 400, textTransform: "none", letterSpacing: 0 }}>(optional)</span>
        </label>
        <textarea
          value={reason}
          onChange={e => setReason(e.target.value)}
          placeholder="e.g. CM unresponsive, bundle reassigned…"
          rows={3}
          style={{ width: "100%", resize: "vertical", fontSize: 13, padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line-2)", background: "var(--surface)", color: "var(--ink)", boxSizing: "border-box" }}
        />
      </div>
      <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
        <button className="btn ghost" onClick={onClose}>Cancel</button>
        <button className="btn" style={{ background: "var(--alert)", color: "#fff", borderColor: "var(--alert)" }} onClick={() => onConfirm(reason.trim())}>
          Drop Bundle
        </button>
      </div>
    </Modal>
  );
}

Object.assign(window, { CampaignsPage, CampaignDetailPage, EditCampaignModal });
