// ---------- Clients ----------

// ── Helpers ───────────────────────────────────────────────────────────────────
function fmtRate(v) {
  if (!v) return "—";
  return `RM ${Number(v).toFixed(2)}/min`;
}

function clientInitials(c) {
  return ((c.name || "?").split(" ").map(w => w[0] || "").join("").slice(0, 2) || "?").toUpperCase();
}

function StatusBadge({ isLocked }) {
  const color = isLocked ? "#dc2626" : "#16a34a";
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 5,
      fontSize: 11, fontWeight: 700, padding: "3px 9px", borderRadius: 99,
      background: isLocked ? "rgba(220,38,38,.10)" : "rgba(22,163,74,.10)",
      color, border: `1px solid ${isLocked ? "rgba(220,38,38,.22)" : "rgba(22,163,74,.22)"}`,
    }}>
      <span style={{ width: 6, height: 6, borderRadius: "50%", background: color, display: "inline-block" }} />
      {isLocked ? "Locked" : "Active"}
    </span>
  );
}

function TempPwBox({ password, onClose }) {
  const [copied, setCopied] = React.useState(false);
  function copy() {
    navigator.clipboard.writeText(password).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); });
  }
  return (
    <div style={{ marginTop: 16, padding: "12px 14px", background: "rgba(22,163,74,.08)", border: "1px solid rgba(22,163,74,.30)", borderRadius: 8 }}>
      <div style={{ fontSize: 11, fontWeight: 700, color: "#16a34a", marginBottom: 6, textTransform: "uppercase", letterSpacing: "0.05em" }}>Temporary Password</div>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <code style={{ flex: 1, fontSize: 15, fontFamily: "var(--font-mono, monospace)", letterSpacing: "0.05em", color: "var(--text1)" }}>{password}</code>
        <button onClick={copy} style={{ fontSize: 11, padding: "4px 10px", borderRadius: 6, border: "1px solid rgba(22,163,74,.35)", background: copied ? "rgba(22,163,74,.15)" : "transparent", color: "#16a34a", cursor: "pointer", fontWeight: 600 }}>
          {copied ? "Copied!" : "Copy"}
        </button>
      </div>
      <div style={{ fontSize: 11, color: "var(--text3)", marginTop: 6 }}>Share this with the client — they can change it on first login.</div>
    </div>
  );
}

const CLIENT_PANEL_OPTIONS = [
  { id: "dashboard", label: "Dashboard", desc: "Overview and daily KPIs." },
  { id: "analytics", label: "Analytics", desc: "Reports, trends, and structured outputs." },
  { id: "billing", label: "Billing", desc: "Wallet, invoices, and spending." },
  { id: "assistants", label: "Assistants", desc: "View the assigned assistant setup." },
  { id: "blast", label: "Blast Campaigns", desc: "Launch outbound campaigns." },
  { id: "logs", label: "Call Logs", desc: "Review transcripts and outcomes." },
];

// ── Add / Edit client modal ───────────────────────────────────────────────────
function ClientModal({ client, assistants, sipTrunks, onSave, onClose }) {
  const isNew = !client;
  const [form, setForm] = React.useState(() => ({
    name: client?.name || "",
    company: client?.company || "",
    email: client?.email || "",
    phone: client?.phone || "",
    industry: client?.industry || "",
    assistantId: client?.assistantId || "",
    pricingMyrPerMin: client?.pricingMyrPerMin ?? "",
    whatsappRateMyr: client?.whatsappRateMyr ?? "",
    otpSmsRateMyr: client?.otpSmsRateMyr ?? "",
    walletEnforced: client?.walletEnforced || false,
    monthlyMinuteLimit: client?.monthlyMinuteLimit ?? 0,
    maxConcurrentCalls: client?.maxConcurrentCalls ?? 2,
    notes: client?.notes || "",
    sipTrunkIds: Array.isArray(client?.sipTrunkIds) ? client.sipTrunkIds : [],
    allowedViews: Array.isArray(client?.allowedViews) && client.allowedViews.length
      ? client.allowedViews
      : CLIENT_PANEL_OPTIONS.map((option) => option.id),
  }));
  const [saving, setSaving] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const [tempPw, setTempPw] = React.useState(null);
  const [balanceMyr, setBalanceMyr] = React.useState(Number(client?.walletBalanceMyr || 0));
  const [topupAmount, setTopupAmount] = React.useState("");
  const [topupNote, setTopupNote] = React.useState("");
  const [topupDate, setTopupDate] = React.useState(() => new Date().toISOString().slice(0, 10));
  const [toppingUp, setToppingUp] = React.useState(false);
  const [topupErr, setTopupErr] = React.useState(null);

  async function submitTopup() {
    setTopupErr(null);
    const amount = Number(topupAmount);
    if (!amount || amount <= 0) { setTopupErr("Enter an amount greater than 0."); return; }
    setToppingUp(true);
    try {
      const res = await voxApi(`/wallet/${client.id}/topup`, { method: "POST", body: JSON.stringify({ amountMyr: amount, note: topupNote.trim(), occurredAt: topupDate ? new Date(topupDate).toISOString() : undefined }) });
      setBalanceMyr(res.entry.balanceAfter);
      setTopupAmount("");
      setTopupNote("");
      setTopupDate(new Date().toISOString().slice(0, 10));
    } catch (e) {
      setTopupErr(e.message);
    } finally {
      setToppingUp(false);
    }
  }

  function set(k, v) { setForm(f => ({ ...f, [k]: v })); }
  function toggleAllowedView(viewId) {
    setForm(f => {
      const current = Array.isArray(f.allowedViews) ? f.allowedViews : [];
      const next = current.includes(viewId)
        ? current.filter(id => id !== viewId)
        : [...current, viewId];
      return { ...f, allowedViews: next };
    });
  }

  const rate = Number(form.pricingMyrPerMin) || 0;
  const preview5 = (rate * 5).toFixed(2);

  async function submit() {
    setErr(null);
    if (!form.name.trim()) { setErr("Name is required."); return; }
    if (!form.email.trim()) { setErr("Email is required."); return; }
    if (!Array.isArray(form.allowedViews) || form.allowedViews.length === 0) { setErr("Select at least one workspace panel."); return; }
    setSaving(true);
    try {
      const body = {
        name: form.name.trim(),
        company: form.company.trim(),
        email: form.email.trim(),
        phone: form.phone.trim(),
        industry: form.industry.trim(),
        assistantId: form.assistantId || null,
        pricingMyrPerMin: Number(form.pricingMyrPerMin) || 0,
        whatsappRateMyr: Number(form.whatsappRateMyr) || 0,
        otpSmsRateMyr: Number(form.otpSmsRateMyr) || 0,
        walletEnforced: Boolean(form.walletEnforced),
        monthlyMinuteLimit: Number(form.monthlyMinuteLimit) || 0,
        maxConcurrentCalls: Math.max(1, Number(form.maxConcurrentCalls) || 2),
        notes: form.notes.trim(),
        allowedViews: form.allowedViews,
        sipTrunkIds: form.sipTrunkIds || [],
      };
      const res = isNew
        ? await voxApi("/client-admin", { method: "POST", body: JSON.stringify(body) })
        : await voxApi(`/client-admin/${client.id}`, { method: "PUT", body: JSON.stringify(body) });
      if (isNew && res.client?.tempPassword) {
        setTempPw(res.client.tempPassword);
      }
      onSave(res.client);
    } catch (e) {
      setErr(e.message);
    } finally {
      setSaving(false);
    }
  }

  const inputStyle = { width: "100%", boxSizing: "border-box" };
  const sectionLabel = { fontSize: 10, fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--text3)", marginBottom: 10, marginTop: 18 };

  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 560, width: "95%" }}>
        <div className="modal-head">
          <div className="card-title">{isNew ? "New client" : `Edit · ${client.name}`}</div>
          <Btn variant="ghost" size="sm" icon="x" onClick={onClose} />
        </div>
        <div className="modal-body" style={{ maxHeight: "72vh", overflowY: "auto" }}>

          {/* Account */}
          <div style={sectionLabel}>Account information</div>
          <div className="fld-row">
            <Field label="Full name *"><Input value={form.name} onChange={e => set("name", e.target.value)} placeholder="Contact name" style={inputStyle} /></Field>
            <Field label="Company"><Input value={form.company} onChange={e => set("company", e.target.value)} placeholder="Organisation" style={inputStyle} /></Field>
          </div>
          <div className="fld-row">
            <Field label="Email *"><Input type="email" value={form.email} onChange={e => set("email", e.target.value)} placeholder="login@client.com" style={inputStyle} /></Field>
            <Field label="Phone"><Input value={form.phone} onChange={e => set("phone", e.target.value)} placeholder="+60 12 345 6789" style={inputStyle} /></Field>
          </div>
          <Field label="Industry">
            <Select value={form.industry} onChange={e => set("industry", e.target.value)} style={inputStyle}>
              <option value="">— none —</option>
              <option value="collections">Collections</option>
              <option value="healthcare">Healthcare</option>
              <option value="automotive">Automotive</option>
              <option value="insurance">Insurance</option>
              <option value="banking">Banking</option>
              <option value="retail">Retail</option>
              <option value="telco">Telco</option>
              <option value="real_estate">Real Estate</option>
              <option value="logistics">Logistics</option>
              <option value="general">General</option>
            </Select>
          </Field>

          {(sipTrunks || []).length > 0 && (
            <Field label="SIP trunks" help="Pick which numbers this client can blast from">
              <div style={{ display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }}>
                {(sipTrunks || []).map(t => {
                  const checked = (form.sipTrunkIds || []).includes(t.id);
                  return (
                    <label key={t.id} style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, cursor: "pointer", padding: "6px 10px", borderRadius: 7, background: checked ? "rgba(var(--accent-rgb,99,102,241),.08)" : "var(--c-surface)", border: `1.5px solid ${checked ? "var(--c-accent)" : "var(--c-line-2)"}`, transition: "all .12s" }}>
                      <input type="checkbox" checked={checked} onChange={() => {
                        set("sipTrunkIds", checked
                          ? (form.sipTrunkIds || []).filter(id => id !== t.id)
                          : [...(form.sipTrunkIds || []), t.id]);
                      }} style={{ width: 14, height: 14, accentColor: "var(--c-accent)" }} />
                      <div style={{ flex: 1 }}>
                        <span style={{ fontWeight: 600 }}>{t.name}</span>
                        <span className="muted" style={{ marginLeft: 6, fontSize: 11 }}>{t.callerId || t.host}</span>
                      </div>
                    </label>
                  );
                })}
              </div>
              {(form.sipTrunkIds || []).length === 0 && <div className="muted text-11 mt-1">None selected — will use the default trunk.</div>}
            </Field>
          )}

          {/* Pricing */}
          <div style={sectionLabel}>Pricing &amp; limits</div>
          <div className="fld-row">
            <Field label="Rate (RM / min)" help="Charged per call minute">
              <Input type="number" step="0.01" min="0" value={form.pricingMyrPerMin} onChange={e => set("pricingMyrPerMin", e.target.value)} placeholder="0.50" style={inputStyle} />
            </Field>
            <Field label="Monthly min limit" help="0 = unlimited">
              <Input type="number" min="0" value={form.monthlyMinuteLimit} onChange={e => set("monthlyMinuteLimit", e.target.value)} style={inputStyle} />
            </Field>
            <Field label="Max concurrent calls">
              <Input type="number" min="1" value={form.maxConcurrentCalls} onChange={e => set("maxConcurrentCalls", e.target.value)} style={inputStyle} />
            </Field>
          </div>
          {rate > 0 && (
            <div style={{ marginTop: 6, padding: "8px 12px", background: "var(--bg-2)", borderRadius: 7, fontSize: 12, color: "var(--text2)", display: "flex", gap: 20 }}>
              <span>RM {rate.toFixed(2)} &times; 5 min = <strong style={{ color: "var(--text1)" }}>RM {preview5}</strong> typical call</span>
              <span>RM {rate.toFixed(2)} &times; 60 min = <strong style={{ color: "var(--text1)" }}>RM {(rate * 60).toFixed(2)}</strong> / hr</span>
            </div>
          )}
          {rate === 0 && (
            <div className="muted text-11 mt-1">No rate set — this client falls back to the auto-markup pricing model instead of a flat per-minute price.</div>
          )}

          <div className="fld-row" style={{ marginTop: 10 }}>
            <Field label="WhatsApp send rate (RM)" help="Charged per WhatsApp blast/message sent">
              <Input type="number" step="0.01" min="0" value={form.whatsappRateMyr} onChange={e => set("whatsappRateMyr", e.target.value)} placeholder="0.40" style={inputStyle} />
            </Field>
            <Field label="OTP / SMS rate (RM)" help="Charged per OTP or SMS sent">
              <Input type="number" step="0.01" min="0" value={form.otpSmsRateMyr} onChange={e => set("otpSmsRateMyr", e.target.value)} placeholder="0.15" style={inputStyle} />
            </Field>
          </div>

          {/* Prepaid wallet */}
          <div style={sectionLabel}>Prepaid wallet</div>
          <label style={{ display: "flex", alignItems: "flex-start", gap: 10, cursor: "pointer" }}>
            <input type="checkbox" checked={form.walletEnforced} onChange={e => set("walletEnforced", e.target.checked)} style={{ width: 14, height: 14, marginTop: 2, accentColor: "var(--c-accent)" }} />
            <div>
              <div style={{ fontSize: 13, fontWeight: 600 }}>Require prepaid balance for outbound</div>
              <div className="muted text-11 mt-1">When enabled, outbound calls/blasts/SMS/WhatsApp are blocked once this client's wallet balance runs out. Inbound calls from their own customers are never blocked. Off by default — existing clients are unaffected until you turn this on.</div>
            </div>
          </label>

          {!isNew && (
            <div style={{ marginTop: 10, padding: "10px 12px", background: "var(--bg-2)", borderRadius: 7 }}>
              <div className="row gap-3" style={{ justifyContent: "space-between", alignItems: "center" }}>
                <div>
                  <div className="text-11 muted">Current balance</div>
                  <div style={{ fontSize: 18, fontWeight: 700, color: balanceMyr > 0 ? "var(--text1)" : "var(--err, #dc2626)" }}>RM {balanceMyr.toFixed(2)}</div>
                </div>
              </div>
              <div className="fld-row" style={{ marginTop: 8 }}>
                <Field label="Top up (RM)">
                  <Input type="number" step="0.01" min="0" value={topupAmount} onChange={e => setTopupAmount(e.target.value)} placeholder="500.00" style={inputStyle} />
                </Field>
                <Field label="Date" help="Backdate if logging a past payment">
                  <Input type="date" value={topupDate} onChange={e => setTopupDate(e.target.value)} style={inputStyle} />
                </Field>
                <Field label="Note (optional)">
                  <Input value={topupNote} onChange={e => setTopupNote(e.target.value)} placeholder="Bank transfer ref #..." style={inputStyle} />
                </Field>
              </div>
              {topupErr && <div className="text-11 mt-1" style={{ color: "var(--err, #dc2626)" }}>{topupErr}</div>}
              <div style={{ marginTop: 8 }}>
                <Btn variant="primary" size="sm" onClick={submitTopup} disabled={toppingUp}>{toppingUp ? "Adding…" : "Add top-up"}</Btn>
              </div>
            </div>
          )}

          {/* Assistant */}
          <div style={sectionLabel}>Assigned assistant</div>
          <Field label="AI assistant">
            <Select value={form.assistantId} onChange={e => set("assistantId", e.target.value)} style={inputStyle}>
              <option value="">— none —</option>
              {assistants.map(a => (
                <option key={a.id} value={a.id}>{a.name}{a.useCase ? ` · ${a.useCase}` : ""}</option>
              ))}
            </Select>
          </Field>

          <div style={sectionLabel}>Workspace panel access</div>
          <div className="card-subtitle" style={{ marginBottom: 10 }}>Choose which client-view panels this user can see.</div>
          <div style={{ display: "grid", gap: 10 }}>
            {CLIENT_PANEL_OPTIONS.map(option => {
              const checked = form.allowedViews.includes(option.id);
              return (
                <label key={option.id} style={{
                  display: "flex",
                  alignItems: "flex-start",
                  gap: 10,
                  padding: "10px 12px",
                  borderRadius: 10,
                  border: "1px solid var(--c-line)",
                  background: checked ? "rgba(124,92,255,.08)" : "var(--c-surface)",
                  cursor: "pointer",
                }}>
                  <input type="checkbox" checked={checked} onChange={() => toggleAllowedView(option.id)} style={{ marginTop: 2 }} />
                  <div>
                    <div className="strong text-12">{option.label}</div>
                    <div className="muted text-11">{option.desc}</div>
                  </div>
                </label>
              );
            })}
          </div>

          {/* Notes */}
          <div style={sectionLabel}>Internal notes</div>
          <Field>
            <Textarea value={form.notes} onChange={e => set("notes", e.target.value)} rows={2} placeholder="Optional notes visible only to admins" style={inputStyle} />
          </Field>

          {/* Temp password after creation */}
          {tempPw && <TempPwBox password={tempPw} />}

          {err && (
            <div style={{ marginTop: 12, padding: "8px 12px", background: "rgba(239,68,68,.10)", border: "1px solid rgba(239,68,68,.28)", borderRadius: 7, color: "#f87171", fontSize: 13 }}>
              {err}
            </div>
          )}
        </div>
        <div className="modal-foot">
          {tempPw
            ? <Btn variant="primary" onClick={onClose}>Done</Btn>
            : (
              <>
                <Btn variant="ghost" onClick={onClose}>Cancel</Btn>
                <Btn variant="primary" onClick={submit} disabled={saving}>{saving ? "Saving…" : isNew ? "Create client" : "Save changes"}</Btn>
              </>
            )
          }
        </div>
      </div>
    </div>
  );
}

// ── Reset password modal ──────────────────────────────────────────────────────
function ResetPasswordModal({ client, onClose }) {
  const [loading, setLoading] = React.useState(false);
  const [tempPw, setTempPw] = React.useState(null);
  const [err, setErr] = React.useState(null);

  async function doReset() {
    setLoading(true);
    setErr(null);
    try {
      const res = await voxApi(`/client-admin/${client.id}/reset-password`, { method: "POST" });
      setTempPw(res.tempPassword);
    } catch (e) {
      setErr(e.message);
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="modal-back" onClick={!tempPw ? onClose : undefined}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 420, width: "95%" }}>
        <div className="modal-head">
          <div className="card-title">Reset password</div>
          <Btn variant="ghost" size="sm" icon="x" onClick={onClose} />
        </div>
        <div className="modal-body">
          {!tempPw ? (
            <>
              <div style={{ fontSize: 13, color: "var(--text2)", lineHeight: 1.5 }}>
                Generate a new temporary password for <strong style={{ color: "var(--text1)" }}>{client.name}</strong>?
                Their current password will be replaced immediately.
              </div>
              {err && (
                <div style={{ marginTop: 10, padding: "8px 12px", background: "rgba(239,68,68,.10)", border: "1px solid rgba(239,68,68,.28)", borderRadius: 7, color: "#f87171", fontSize: 13 }}>
                  {err}
                </div>
              )}
            </>
          ) : (
            <>
              <div style={{ fontSize: 13, color: "var(--text2)", marginBottom: 4 }}>Password reset successfully.</div>
              <TempPwBox password={tempPw} />
            </>
          )}
        </div>
        <div className="modal-foot">
          {!tempPw
            ? (
              <>
                <Btn variant="ghost" onClick={onClose}>Cancel</Btn>
                <Btn variant="primary" onClick={doReset} disabled={loading}>{loading ? "Resetting…" : "Reset password"}</Btn>
              </>
            )
            : <Btn variant="primary" onClick={onClose}>Done</Btn>
          }
        </div>
      </div>
    </div>
  );
}

// ── Admins panel (unchanged from original) ────────────────────────────────────
function AdminsPanel() {
  const [admins, setAdmins] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState(null);
  const [showNew, setShowNew] = React.useState(false);
  const [form, setForm] = React.useState({ name: "", email: "", password: "" });
  const [formErr, setFormErr] = React.useState(null);
  const [saving, setSaving] = React.useState(false);
  const [deletingId, setDeletingId] = React.useState(null);

  async function loadAdmins() {
    try {
      setErr(null);
      const data = await voxApi("/auth/admins");
      setAdmins(data.admins || []);
    } catch (e) {
      setErr(e.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => { loadAdmins(); }, []);

  async function createAdmin() {
    if (!form.name || !form.email || !form.password) { setFormErr("All fields are required."); return; }
    setSaving(true);
    setFormErr(null);
    try {
      await voxApi("/auth/admins", { method: "POST", body: JSON.stringify(form) });
      setShowNew(false);
      setForm({ name: "", email: "", password: "" });
      loadAdmins();
    } catch (e) {
      setFormErr(e.message);
    } finally {
      setSaving(false);
    }
  }

  async function deleteAdmin(id) {
    if (!window.confirm("Remove this admin account?")) return;
    setDeletingId(id);
    try {
      await voxApi(`/auth/admins/${id}`, { method: "DELETE" });
      setAdmins(a => a.filter(x => x.id !== id));
    } catch (e) {
      alert(e.message);
    } finally {
      setDeletingId(null);
    }
  }

  if (loading) return <div className="muted text-12" style={{ padding: 24 }}>Loading…</div>;
  if (err) return <div style={{ padding: 24, color: "var(--err)", fontSize: 13 }}>{err}</div>;

  return (
    <>
      {showNew && (
        <div className="modal-back" onClick={() => setShowNew(false)}>
          <div className="modal" onClick={e => e.stopPropagation()}>
            <div className="modal-head">
              <div className="card-title">New admin account</div>
              <Btn variant="ghost" size="sm" icon="x" onClick={() => setShowNew(false)} />
            </div>
            <div className="modal-body">
              <Field label="Name"><Input value={form.name} onChange={e => setForm(f => ({ ...f, name: e.target.value }))} placeholder="Full name" /></Field>
              <Field label="Email"><Input type="email" value={form.email} onChange={e => setForm(f => ({ ...f, email: e.target.value }))} placeholder="admin@company.com" /></Field>
              <Field label="Password"><Input type="password" value={form.password} onChange={e => setForm(f => ({ ...f, password: e.target.value }))} placeholder="Minimum 8 characters" /></Field>
              {formErr && (
                <div style={{ marginTop: 10, padding: "8px 12px", background: "rgba(239,68,68,.10)", border: "1px solid rgba(239,68,68,.28)", borderRadius: 7, color: "#f87171", fontSize: 13 }}>
                  {formErr}
                </div>
              )}
            </div>
            <div className="modal-foot">
              <Btn variant="ghost" onClick={() => setShowNew(false)}>Cancel</Btn>
              <Btn variant="primary" onClick={createAdmin} disabled={saving}>{saving ? "Creating…" : "Create admin"}</Btn>
            </div>
          </div>
        </div>
      )}

      <Card pad="none">
        <div className="card-head">
          <span className="card-title">{admins.length} admin{admins.length !== 1 ? "s" : ""}</span>
          <Btn variant="ghost" size="sm" icon="plus" onClick={() => { setShowNew(true); setFormErr(null); }}>Add admin</Btn>
        </div>
        {admins.length === 0 ? (
          <div className="empty-state" style={{ padding: 40 }}>
            <div className="empty-state-title">No extra admins</div>
            <div>The env-based admin account is always active. Add accounts here for teammates.</div>
          </div>
        ) : (
          <table style={{ width: "100%", borderCollapse: "collapse" }}>
            <thead>
              <tr style={{ borderBottom: "1px solid var(--c-line)" }}>
                {["Name", "Email", "Created", ""].map(h => (
                  <th key={h} style={{ padding: "10px 16px", textAlign: "left", fontSize: 11, fontWeight: 500, color: "var(--text2)", textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {admins.map(a => (
                <tr key={a.id} style={{ borderBottom: "1px solid var(--c-line)" }}>
                  <td style={{ padding: "12px 16px" }}>
                    <div className="row gap-3">
                      <div className="agent-avatar" style={{ width: 30, height: 30, fontSize: 11, borderRadius: 7, flexShrink: 0 }}>{(a.name || "?").slice(0, 2).toUpperCase()}</div>
                      <span className="strong text-13">{a.name}</span>
                    </div>
                  </td>
                  <td style={{ padding: "12px 16px", fontSize: 13, color: "var(--text2)" }}>{a.email}</td>
                  <td style={{ padding: "12px 16px", fontSize: 12, color: "var(--text2)" }}>{fmtDate(a.createdAt)}</td>
                  <td style={{ padding: "12px 16px", textAlign: "right" }}>
                    <Btn variant="ghost" size="sm" icon="trash" style={{ color: "var(--err)" }} disabled={deletingId === a.id} onClick={() => deleteAdmin(a.id)}>
                      {deletingId === a.id ? "Removing…" : "Remove"}
                    </Btn>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
        <div style={{ padding: "12px 16px", borderTop: admins.length ? "1px solid var(--c-line)" : "none", fontSize: 11, color: "var(--text3)" }}>
          The primary admin set in <span className="mono">.env</span> (ADMIN_EMAIL / ADMIN_PASSWORD) is always active and cannot be managed from this panel.
        </div>
      </Card>
    </>
  );
}

// ── Root view ─────────────────────────────────────────────────────────────────
const ClientsView = ({ assistants = [], setClients, mode = "dev" }) => {
  const [tab, setTab] = React.useState("clients");
  const [clientCount, setClientCount] = React.useState(0);

  function handleCountChange(n) {
    setClientCount(n);
    // Keep app-level clients state length in sync (sidebar badge uses .length)
    if (setClients) setClients(Array(n).fill(null));
  }

  const TABS = [
    { id: "clients", label: "Clients", count: clientCount },
    { id: "admins", label: "Admins", devOnly: true },
  ].filter(t => mode === "dev" || !t.devOnly);

  return (
    <div>
      <div className="ph">
        <div>
          <h1 className="ph-title">Clients</h1>
          <div className="ph-subtitle">Manage client accounts, assistant assignments, and per-minute pricing.</div>
        </div>
      </div>

      {/* Tabs */}
      <div style={{ display: "flex", gap: 0, marginBottom: 20, borderBottom: "1px solid var(--c-line)" }}>
        {TABS.map(t => (
          <button key={t.id} onClick={() => setTab(t.id)} style={{
            padding: "8px 20px", background: "none", border: "none",
            borderBottom: tab === t.id ? "2px solid var(--accent)" : "2px solid transparent",
            color: tab === t.id ? "var(--text1)" : "var(--text2)",
            fontFamily: "inherit", fontSize: 13, fontWeight: tab === t.id ? 600 : 400,
            cursor: "pointer", marginBottom: -1, transition: "color 0.15s",
            display: "flex", alignItems: "center", gap: 7,
          }}>
            {t.label}
            {t.count > 0 && (
              <span style={{ fontSize: 10, fontWeight: 700, padding: "1px 6px", borderRadius: 99, background: tab === t.id ? "var(--a-line)" : "var(--bg-2)", color: tab === t.id ? "var(--a)" : "var(--text3)" }}>
                {t.count}
              </span>
            )}
          </button>
        ))}
      </div>

      {tab === "clients" && (
        <ClientsPanelWrapper assistants={assistants} onCountChange={handleCountChange} />
      )}
      {tab === "admins" && mode === "dev" && <AdminsPanel />}
    </div>
  );
};

// Wrapper that exposes the "New client" button trigger to the page header
// We use a context approach so the Add button lives in ph-actions but triggers modal in the panel
const ClientsPanelContext = React.createContext({});

function ClientsPanelWrapper({ assistants, onCountChange }) {
  const [showAdd, setShowAdd] = React.useState(false);
  const [clients, setClients] = React.useState([]);
  const [sipTrunks, setSipTrunks] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState(null);
  const [filter, setFilter] = React.useState("all");
  const [search, setSearch] = React.useState("");
  const [editClient, setEditClient] = React.useState(null);
  const [resetClient, setResetClient] = React.useState(null);
  const [deletingId, setDeletingId] = React.useState(null);

  async function load() {
    try {
      setErr(null);
      const res = await voxApi("/client-admin");
      const list = res.clients || [];
      setClients(list);
      onCountChange && onCountChange(list.length);
    } catch (e) {
      setErr(e.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
    voxApiOptional("/sip/trunks", { trunks: [] }).then(d => setSipTrunks(d.trunks || []));
  }, []);

  // Expose add trigger globally so ph-actions button can open modal
  React.useEffect(() => {
    window.__clientsAddTrigger = () => setShowAdd(true);
    return () => { delete window.__clientsAddTrigger; };
  }, []);

  async function toggleLock(client) {
    try {
      const res = await voxApi(`/client-admin/${client.id}`, {
        method: "PUT",
        body: JSON.stringify({ isLocked: !client.isLocked }),
      });
      setClients(cs => cs.map(c => c.id === client.id ? res.client : c));
    } catch (e) {
      alert(e.message);
    }
  }

  async function deleteClient(client) {
    if (!window.confirm(`Remove client "${client.name}"? This cannot be undone.`)) return;
    setDeletingId(client.id);
    try {
      await voxApi(`/client-admin/${client.id}`, { method: "DELETE" });
      setClients(cs => {
        const next = cs.filter(c => c.id !== client.id);
        onCountChange && onCountChange(next.length);
        return next;
      });
    } catch (e) {
      alert(e.message);
    } finally {
      setDeletingId(null);
    }
  }

  function handleSaved(updated) {
    if (!updated) return;
    setClients(cs => {
      const idx = cs.findIndex(c => c.id === updated.id);
      const next = idx >= 0
        ? cs.map(c => c.id === updated.id ? { ...c, ...updated } : c)
        : [...cs, updated];
      onCountChange && onCountChange(next.length);
      return next;
    });
    // Keep modal open on new client to show temp password; close on edit
    if (!updated.tempPassword) {
      setShowAdd(false);
      setEditClient(null);
    }
  }

  const active = clients.filter(c => !c.isLocked).length;
  const locked = clients.filter(c => c.isLocked).length;

  const FILTERS = [
    { id: "all", label: `All (${clients.length})` },
    { id: "active", label: `Active (${active})` },
    { id: "locked", label: `Locked (${locked})` },
  ];

  const visible = clients.filter(c => {
    if (filter === "active" && c.isLocked) return false;
    if (filter === "locked" && !c.isLocked) return false;
    if (search) {
      const q = search.toLowerCase();
      return (c.name || "").toLowerCase().includes(q)
        || (c.company || "").toLowerCase().includes(q)
        || (c.email || "").toLowerCase().includes(q);
    }
    return true;
  });

  function assistantName(id) {
    const a = assistants.find(x => x.id === id);
    return a ? a.name : id ? id.slice(-8) : null;
  }

  if (loading) return <div className="muted text-12" style={{ padding: 32 }}>Loading clients…</div>;
  if (err) return <div style={{ padding: 32, color: "var(--err)", fontSize: 13 }}>{err} <button onClick={load} style={{ marginLeft: 8, fontSize: 12, textDecoration: "underline", background: "none", border: "none", cursor: "pointer", color: "var(--a)" }}>Retry</button></div>;

  const thStyle = { padding: "9px 14px", textAlign: "left", fontSize: 11, fontWeight: 600, color: "var(--text3)", textTransform: "uppercase", letterSpacing: "0.06em", whiteSpace: "nowrap" };
  const tdStyle = { padding: "11px 14px", borderBottom: "1px solid var(--c-line)", verticalAlign: "middle" };

  return (
    <>
      {(showAdd || editClient) && (
        <ClientModal
          client={editClient || null}
          assistants={assistants}
          sipTrunks={sipTrunks}
          onSave={handleSaved}
          onClose={() => { setShowAdd(false); setEditClient(null); }}
        />
      )}
      {resetClient && (
        <ResetPasswordModal client={resetClient} onClose={() => setResetClient(null)} />
      )}

      {/* Filter + Search bar */}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14, gap: 12, flexWrap: "wrap" }}>
        <div style={{ display: "flex", gap: 0, background: "var(--bg-2)", borderRadius: 8, padding: 3, border: "1px solid var(--c-line)" }}>
          {FILTERS.map(f => (
            <button key={f.id} onClick={() => setFilter(f.id)} style={{
              padding: "5px 14px", border: "none", borderRadius: 6,
              background: filter === f.id ? "var(--surface)" : "transparent",
              boxShadow: filter === f.id ? "0 1px 3px rgba(0,0,0,.08)" : "none",
              color: filter === f.id ? "var(--text1)" : "var(--text3)",
              fontSize: 12, fontWeight: filter === f.id ? 600 : 400,
              cursor: "pointer", fontFamily: "inherit", transition: "all .12s",
            }}>{f.label}</button>
          ))}
        </div>

        <div style={{ display: "flex", gap: 8, flex: "1 1 auto", justifyContent: "flex-end", alignItems: "center" }}>
          <div style={{ position: "relative", flex: "1 1 200px", maxWidth: 280 }}>
            <span style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", color: "var(--text3)", pointerEvents: "none" }}>
              <Icon name="search" style={{ width: 14, height: 14 }} />
            </span>
            <input
              value={search}
              onChange={e => setSearch(e.target.value)}
              placeholder="Search clients…"
              style={{ width: "100%", boxSizing: "border-box", padding: "7px 10px 7px 32px", border: "1px solid var(--c-line)", borderRadius: 8, fontSize: 13, background: "var(--surface)", color: "var(--text1)", outline: "none", fontFamily: "inherit" }}
            />
          </div>
          <Btn variant="ghost" size="sm" onClick={load} icon="refresh" title="Refresh" />
          <Btn variant="primary" size="sm" icon="plus" onClick={() => { setEditClient(null); setShowAdd(true); }}>Add client</Btn>
        </div>
      </div>

      {/* Table */}
      <div className="card" style={{ padding: 0, overflow: "hidden" }}>
        <table style={{ width: "100%", borderCollapse: "collapse" }}>
          <thead style={{ background: "var(--bg-2)" }}>
            <tr>
              <th style={thStyle}>Client</th>
              <th style={thStyle}>Email</th>
              <th style={thStyle}>Status</th>
              <th style={thStyle}>Assistant</th>
              <th style={thStyle}>Rate</th>
              <th style={{ ...thStyle, textAlign: "right" }}>Actions</th>
            </tr>
          </thead>
          <tbody>
            {visible.length === 0 && (
              <tr>
                <td colSpan={6} style={{ padding: "52px 20px", textAlign: "center", color: "var(--text3)", fontSize: 13 }}>
                  {search ? `No clients match "${search}"` : "No clients yet — click Add client to get started."}
                </td>
              </tr>
            )}
            {visible.map(c => (
              <tr key={c.id}
                onMouseEnter={e => e.currentTarget.style.background = "var(--bg-2)"}
                onMouseLeave={e => e.currentTarget.style.background = ""}>
                <td style={tdStyle}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                    <div className="agent-avatar" style={{ width: 32, height: 32, fontSize: 11, borderRadius: 8, flexShrink: 0 }}>{clientInitials(c)}</div>
                    <div>
                      <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text1)" }}>{c.name}</div>
                      {c.company && <div style={{ fontSize: 11, color: "var(--text3)", marginTop: 1 }}>{c.company}</div>}
                    </div>
                  </div>
                </td>
                <td style={{ ...tdStyle, fontSize: 13, color: "var(--text2)" }}>{c.email}</td>
                <td style={tdStyle}><StatusBadge isLocked={c.isLocked} /></td>
                <td style={{ ...tdStyle, fontSize: 12, color: "var(--text2)" }}>
                  {c.assistantId
                    ? <span style={{ display: "flex", alignItems: "center", gap: 5 }}>
                        <span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--a)", display: "inline-block" }} />
                        {assistantName(c.assistantId)}
                      </span>
                    : <span style={{ color: "var(--text4)" }}>—</span>}
                </td>
                <td style={{ ...tdStyle, fontSize: 12 }}>
                  {c.pricingMyrPerMin
                    ? <span style={{ fontFamily: "var(--font-mono, monospace)", fontWeight: 600, color: "var(--text1)" }}>{fmtRate(c.pricingMyrPerMin)}</span>
                    : <span style={{ color: "var(--text4)" }}>—</span>}
                </td>
                <td style={{ ...tdStyle, textAlign: "right" }}>
                  <div style={{ display: "inline-flex", gap: 2, alignItems: "center" }}>
                    {/* Edit */}
                    <button title="Edit client" onClick={() => { setEditClient(c); setShowAdd(false); }}
                      style={{ padding: "5px 8px", border: "1px solid var(--c-line)", borderRadius: 6, background: "transparent", cursor: "pointer", color: "var(--text2)", display: "flex", alignItems: "center", transition: "all .12s" }}>
                      <Icon name="edit" style={{ width: 13, height: 13 }} />
                    </button>
                    {/* Reset password */}
                    <button title="Reset password" onClick={() => setResetClient(c)}
                      style={{ padding: "5px 8px", border: "1px solid var(--c-line)", borderRadius: 6, background: "transparent", cursor: "pointer", color: "var(--text2)", display: "flex", alignItems: "center" }}>
                      <Icon name="key" style={{ width: 13, height: 13 }} />
                    </button>
                    {/* Lock / Unlock */}
                    <button title={c.isLocked ? "Unlock account" : "Lock account"} onClick={() => toggleLock(c)}
                      style={{ padding: "5px 8px", border: "1px solid var(--c-line)", borderRadius: 6, background: "transparent", cursor: "pointer", color: c.isLocked ? "#16a34a" : "#d97706", display: "flex", alignItems: "center" }}>
                      <Icon name={c.isLocked ? "unlock" : "lock"} style={{ width: 13, height: 13 }} />
                    </button>
                    {/* Delete */}
                    <button title="Delete" onClick={() => deleteClient(c)} disabled={deletingId === c.id}
                      style={{ padding: "5px 8px", border: "1px solid var(--c-line)", borderRadius: 6, background: "transparent", cursor: "pointer", color: "var(--err)", display: "flex", alignItems: "center", opacity: deletingId === c.id ? 0.5 : 1 }}>
                      <Icon name="trash" style={{ width: 13, height: 13 }} />
                    </button>
                  </div>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {clients.length > 0 && (
        <div style={{ marginTop: 8, fontSize: 11, color: "var(--text3)", paddingLeft: 2 }}>
          {visible.length} of {clients.length} client{clients.length !== 1 ? "s" : ""} shown
        </div>
      )}
    </>
  );
}

window.ClientsView = ClientsView;
