// ---------- Tools, Automations, System ----------

const BLANK_WEBHOOK = { type: "webhook", name: "", webhookUrl: "", method: "POST", description: "", headersJson: "", bodySchemaJson: "" };
const BLANK_TRANSFER = { type: "transferCall", name: "", description: "", transferDestination: "", messageToCustomer: "", messageToOperator: "", callerId: "", transferMode: "blind" };
const genId = () => (typeof crypto !== "undefined" && crypto.randomUUID) ? crypto.randomUUID() : Math.random().toString(36).slice(2) + Date.now().toString(36);
const BLANK_QUERY = { type: "query", name: "", description: "", knowledgeBases: [{ id: genId(), name: "", description: "", files: [] }] };

function toolSlug(name) { return String(name || "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "tool_id"; }

function isValidTool(tool) {
  if (!tool.name || !String(tool.name).trim()) return false;
  if (tool.type === "transferCall") return Boolean(tool.transferDestination && String(tool.transferDestination).trim());
  if (tool.type === "query") return (tool.knowledgeBases || []).some(kb => kb.name && kb.name.trim());
  return Boolean(tool.webhookUrl && String(tool.webhookUrl).trim());
}

const QueryKbSection = ({ tool, setTool, onUpload, onDeleteFile }) => {
  const kbs = tool.knowledgeBases || [];
  const addKb = () => setTool(d => ({ ...d, knowledgeBases: [...(d.knowledgeBases || []), { id: genId(), name: "", description: "", files: [] }] }));
  const removeKb = (idx) => setTool(d => ({ ...d, knowledgeBases: (d.knowledgeBases || []).filter((_, i) => i !== idx) }));
  const updateKb = (idx, field, val) => setTool(d => ({
    ...d,
    knowledgeBases: (d.knowledgeBases || []).map((kb, i) => i === idx ? { ...kb, [field]: val } : kb)
  }));

  return (
    <div style={{ marginTop: 8 }}>
      <div className="row" style={{ justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
        <span style={{ fontSize: 12, fontWeight: 600, color: "var(--c-text-2)" }}>Knowledge Bases</span>
        <Btn variant="ghost" size="sm" icon="plus" onClick={addKb}>Add KB</Btn>
      </div>
      {kbs.map((kb, idx) => (
        <div key={kb.id || idx} style={{ border: "1px solid var(--c-line-2)", borderRadius: 10, padding: "12px 14px", marginBottom: 10, background: "var(--c-surface)" }}>
          <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-start" }}>
            <div style={{ flex: 1, marginRight: 8 }}>
              <div className="fld-row" style={{ marginBottom: 6 }}>
                <Field label="Name" help="This is the dataset name your system prompt references (e.g. DbAngkasa)">
                  <Input value={kb.name} onChange={e => updateKb(idx, "name", e.target.value)} placeholder="e.g. DbAngkasa" />
                </Field>
              </div>
              <Field label="Description">
                <Input value={kb.description || ""} onChange={e => updateKb(idx, "description", e.target.value)} placeholder="What records does this file contain?" />
              </Field>
            </div>
            {kbs.length > 1 && <Btn variant="ghost" size="sm" icon="trash-2" onClick={() => removeKb(idx)} />}
          </div>

          {/* Files section — only show after KB is saved (has tool.id and kb.id) */}
          {tool.id && kb.id && (
            <div style={{ marginTop: 10 }}>
              <div className="row" style={{ justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
                <span style={{ fontSize: 11, color: "var(--c-text-3)" }}>Files ({(kb.files || []).length})</span>
                <label style={{ cursor: "pointer" }}>
                  <input type="file" accept=".csv,.xlsx,.xls" style={{ display: "none" }}
                    onChange={e => { if (e.target.files[0]) onUpload(tool.id, kb, e.target.files[0]); e.target.value = ""; }} />
                  <span className="btn btn-ghost btn-sm"><Icon name="upload" style={{ width: 13, height: 13, marginRight: 4 }} />Upload CSV / Excel</span>
                </label>
              </div>
              {(kb.files || []).length === 0 ? (
                <div style={{ fontSize: 11, color: "var(--c-text-4)", padding: "6px 0" }}>No files yet — upload a CSV or Excel file to power this knowledge base.</div>
              ) : (
                <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                  {(kb.files || []).map(f => (
                    <div key={f.fileId} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", fontSize: 12, padding: "4px 8px", borderRadius: 6, background: "var(--c-surface-2)" }}>
                      <div className="row gap-2">
                        <Icon name="file-text" style={{ width: 13, height: 13, color: "var(--ok)" }} />
                        <span className="truncate" style={{ maxWidth: 220 }}>{f.originalName}</span>
                        <span style={{ color: "var(--c-text-4)", fontSize: 11 }}>{f.rowCount != null ? `${f.rowCount} rows` : ""}</span>
                      </div>
                      <Btn variant="ghost" size="sm" icon="x" onClick={() => onDeleteFile(tool.id, kb, f.fileId)} />
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}
          {tool.id && !kb.id && (
            <div style={{ marginTop: 8, fontSize: 11, color: "var(--c-text-4)" }}>Save the tool to enable file upload for this KB.</div>
          )}
          {!tool.id && (
            <div style={{ marginTop: 8, fontSize: 11, color: "var(--c-text-4)" }}>Save the tool first, then open Edit to upload files.</div>
          )}
        </div>
      ))}
    </div>
  );
};

const ToolForm = ({ tool, setTool, onSave, onCancel, isEdit, onUpload, onDeleteFile }) => {
  const isTransfer = tool.type === "transferCall";
  const isQuery = tool.type === "query";
  const toolId = tool.toolId || toolSlug(tool.name);
  return (
    <div className="modal-back" onClick={onCancel}>
      <div className="modal" style={{ maxWidth: 560 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div className="card-title">{isEdit ? "Edit tool" : "New tool"}</div>
          <Btn variant="ghost" size="sm" icon="x" onClick={onCancel} />
        </div>
        <div className="modal-body">
          {!isEdit && (
            <Field label="Tool type">
              <div className="row gap-2">
                {[
                  { v: "webhook", label: "Webhook" },
                  { v: "transferCall", label: "Transfer Call" },
                  { v: "query", label: "Query (Knowledge Base)" },
                ].map(opt => (
                  <button key={opt.v} type="button"
                    onClick={() => setTool(opt.v === "transferCall" ? { ...BLANK_TRANSFER } : opt.v === "query" ? { ...BLANK_QUERY } : { ...BLANK_WEBHOOK })}
                    style={{
                      flex: 1, padding: "8px 10px", borderRadius: 8, cursor: "pointer", fontSize: 12, fontWeight: 500, transition: "all .15s",
                      background: tool.type === opt.v ? "var(--c-accent)" : "var(--c-surface)",
                      color: tool.type === opt.v ? "#fff" : "var(--c-fg)",
                      border: tool.type === opt.v ? "1.5px solid var(--c-accent)" : "1.5px solid var(--c-line-2)"
                    }}>
                    {opt.label}
                  </button>
                ))}
              </div>
            </Field>
          )}

          <div className="fld-row">
            <Field label="Tool name">
              <Input value={tool.name} onChange={e => setTool(d => ({ ...d, name: e.target.value }))}
                placeholder={isTransfer ? "e.g. Transfer to Fauzan" : isQuery ? "e.g. query_tool" : "e.g. Check Balance"} />
            </Field>
            {(isQuery || isTransfer) ? null : (
              <Field label="Tool ID" help="Used in system prompt. Auto-derived from name.">
                <Input value={tool.toolId || ""} onChange={e => setTool(d => ({ ...d, toolId: e.target.value }))} placeholder={toolId} />
              </Field>
            )}
          </div>

          <Field label="Description">
            <Input value={tool.description || ""} onChange={e => setTool(d => ({ ...d, description: e.target.value }))}
              placeholder={isTransfer ? "When should the assistant use this transfer?" : isQuery ? "What records can this tool look up?" : "What does this tool do?"} />
          </Field>

          {isTransfer && (
            <>
              <Field label="Transfer destination (phone number or SIP URI)">
                <Input value={tool.transferDestination || ""} onChange={e => setTool(d => ({ ...d, transferDestination: e.target.value }))} placeholder="+60123456789 or sip:user@domain.com" />
              </Field>
              <Field label="Message to customer (spoken before transfer)">
                <Textarea value={tool.messageToCustomer || ""} onChange={e => setTool(d => ({ ...d, messageToCustomer: e.target.value }))}
                  placeholder="Please hold while I connect you to a specialist." style={{ minHeight: 72 }} />
              </Field>
              <Field label="Message to operator (played when agent picks up)">
                <Input value={tool.messageToOperator || ""} onChange={e => setTool(d => ({ ...d, messageToOperator: e.target.value }))} placeholder="e.g. Incoming transferred call from VoxPro AI agent." />
              </Field>
              <div className="fld-row">
                <Field label="Transfer mode">
                  <Select value={tool.transferMode || "blind"} onChange={e => setTool(d => ({ ...d, transferMode: e.target.value }))}>
                    <option value="blind">Blind (SIP REFER)</option>
                  </Select>
                </Field>
                <Field label="Caller ID override (optional)">
                  <Input value={tool.callerId || ""} onChange={e => setTool(d => ({ ...d, callerId: e.target.value }))} placeholder="+60312345678" />
                </Field>
              </div>
              <div style={{ background: "var(--c-surface)", border: "1px solid var(--c-line-2)", borderRadius: 8, padding: "10px 14px", fontSize: 12, color: "var(--c-muted)" }}>
                <strong style={{ color: "var(--c-fg)" }}>How to use:</strong> Add <code style={{ background: "rgba(0,0,0,.06)", borderRadius: 4, padding: "1px 5px" }}>[[TRANSFER:{toolSlug(tool.name)}]]</code> in your system prompt.
              </div>
            </>
          )}

          {!isTransfer && !isQuery && (
            <>
              <Field label="Webhook URL">
                <Input value={tool.webhookUrl || ""} onChange={e => setTool(d => ({ ...d, webhookUrl: e.target.value }))} placeholder="https://your-api.com/tool" />
              </Field>
              <div className="fld-row">
                <Field label="Method">
                  <Select value={tool.method || "POST"} onChange={e => setTool(d => ({ ...d, method: e.target.value }))}>
                    <option value="POST">POST</option>
                    <option value="GET">GET</option>
                  </Select>
                </Field>
              </div>
              <Field label="Request headers (JSON)" help='Optional. e.g. {"x-api-key":"secret","Authorization":"Bearer token"}'>
                <Input value={tool.headersJson || ""} onChange={e => setTool(d => ({ ...d, headersJson: e.target.value }))} placeholder='{"x-partner-key": "your-key-here"}' />
              </Field>
              <div style={{ background: "var(--c-surface)", border: "1px solid var(--c-line-2)", borderRadius: 8, padding: "10px 14px", fontSize: 12, color: "var(--c-muted)" }}>
                <strong style={{ color: "var(--c-fg)" }}>How to use:</strong> Tool ID: <code style={{ background: "rgba(0,0,0,.06)", borderRadius: 4, padding: "1px 5px" }}>{toolId}</code>. The LLM calls it as <code style={{ background: "rgba(0,0,0,.06)", borderRadius: 4, padding: "1px 5px" }}>{`[[tool:${toolId} {"key":"value"}]]`}</code>.
              </div>
            </>
          )}

          {isQuery && (
            <>
              <QueryKbSection tool={tool} setTool={setTool} onUpload={onUpload} onDeleteFile={onDeleteFile} />
              <div style={{ background: "var(--c-surface)", border: "1px solid var(--c-line-2)", borderRadius: 8, padding: "10px 14px", fontSize: 12, color: "var(--c-muted)", marginTop: 10 }}>
                <strong style={{ color: "var(--c-fg)" }}>Tool ID:</strong> <code style={{ background: "rgba(0,0,0,.06)", borderRadius: 4, padding: "1px 5px" }}>{tool.toolId || toolSlug(tool.name)}</code>
                <br /><strong style={{ color: "var(--c-fg)" }}>System prompt call syntax:</strong>
                <br /><code style={{ background: "rgba(0,0,0,.06)", borderRadius: 4, padding: "1px 5px", wordBreak: "break-all" }}>{`[[tool:${tool.toolId || toolSlug(tool.name)} {"query": "<KbName> FIELD:value FIELD:value"}]]`}</code>
              </div>
            </>
          )}
        </div>
        <div className="modal-foot">
          <Btn variant="ghost" onClick={onCancel}>Cancel</Btn>
          <Btn variant="primary" onClick={() => onSave(tool, isEdit)} disabled={!isValidTool(tool)}>Save tool</Btn>
        </div>
      </div>
    </div>
  );
};

const ToolsView = () => {
  const [tools, setTools] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [showNew, setShowNew] = React.useState(false);
  const [newTool, setNewTool] = React.useState(BLANK_WEBHOOK);
  const [editTool, setEditTool] = React.useState(null);
  const [playTool, setPlayTool] = React.useState("");
  const [payload, setPayload] = React.useState('{\n  "key": "value"\n}');
  const [response, setResponse] = React.useState(null);
  const [testing, setTesting] = React.useState(false);
  const [deleting, setDeleting] = React.useState(null);
  const [otpTestTool, setOtpTestTool] = React.useState(null);
  const [otpTestPhone, setOtpTestPhone] = React.useState("");
  const [otpTesting, setOtpTesting] = React.useState(false);

  async function reload() {
    const d = await voxApiOptional("/tools/config", { tools: [] });
    setTools(d.tools || []);
  }

  React.useEffect(() => {
    reload().finally(() => setLoading(false));
  }, []);

  async function saveTool(tool, isEdit) {
    if (!isValidTool(tool)) return;
    try {
      const saved = await voxApi("/tools/config", { method: "POST", body: JSON.stringify(tool) });
      if (isEdit) {
        setTools(t => t.map(x => x.id === saved.tool?.id ? saved.tool : x));
        setEditTool(saved.tool);
      } else {
        await reload();
        setShowNew(false);
        setNewTool({ ...BLANK_WEBHOOK });
        // If it's a query tool, open edit immediately so user can upload files
        if (tool.type === "query") {
          const fresh = await voxApiOptional("/tools/config", { tools: [] });
          const created = (fresh.tools || []).find(t => t.name === tool.name && t.type === "query");
          if (created) setEditTool(created);
        }
      }
    } catch (e) {
      alert("Failed to save tool: " + e.message);
    }
  }

  async function uploadFile(toolId, kb, file) {
    const form = new FormData();
    form.append("file", file);
    try {
      let authHeader = {};
      try { const s = JSON.parse(localStorage.getItem("voxpro.session") || "null"); if (s?.token) authHeader = { Authorization: `Bearer ${s.token}` }; } catch {}
      const res = await fetch(`/tools/config/${toolId}/kb/${kb.id || kb.name}/upload`, { method: "POST", headers: authHeader, body: form });
      if (!res.ok) { const j = await res.json().catch(() => ({})); throw new Error(j.error || res.statusText); }
      const updated = (await voxApiOptional("/tools/config", { tools: [] })).tools || [];
      setTools(updated);
      const fresh = updated.find(t => t.id === toolId);
      if (fresh) setEditTool(fresh);
    } catch (e) {
      alert("Upload failed: " + e.message);
    }
  }

  async function deleteFile(toolId, kb, fileId) {
    if (!confirm("Delete this file?")) return;
    try {
      await voxApi(`/tools/config/${toolId}/kb/${kb.id || kb.name}/files/${fileId}`, { method: "DELETE" });
      const updated = (await voxApiOptional("/tools/config", { tools: [] })).tools || [];
      setTools(updated);
      const fresh = updated.find(t => t.id === toolId);
      if (fresh && editTool?.id === toolId) setEditTool(fresh);
    } catch (e) {
      alert("Delete failed: " + e.message);
    }
  }

  async function removeTool(id) {
    if (!confirm("Delete this tool?")) return;
    setDeleting(id);
    try {
      await voxApi(`/tools/config/${id}`, { method: "DELETE" });
      setTools(t => t.filter(x => x.id !== id));
    } catch (e) {
      alert("Failed to delete: " + e.message);
    } finally {
      setDeleting(null);
    }
  }

  async function testTool() {
    const tool = tools.find(t => t.id === playTool);
    if (!tool) return;
    setTesting(true);
    setResponse(null);
    try {
      let parsedPayload;
      try { parsedPayload = JSON.parse(payload); } catch { parsedPayload = {}; }
      const res = await voxApi("/tools/test", {
        method: "POST",
        body: JSON.stringify({ toolId: tool.toolId || tool.id, payload: parsedPayload })
      });
      setResponse({ ok: true, data: res });
    } catch (e) {
      setResponse({ ok: false, data: { error: e.message } });
    } finally {
      setTesting(false);
    }
  }

  async function testOtpTool() {
    if (!otpTestTool || !otpTestPhone.trim()) return;
    setOtpTesting(true);
    setResponse(null);
    try {
      const res = await voxApi("/tools/test", {
        method: "POST",
        body: JSON.stringify({
          toolId: otpTestTool.toolId || otpTestTool.id,
          payload: { phone: otpTestPhone.trim() },
        }),
      });
      setResponse({ ok: !!res.ok, data: res });
      if (res.ok) {
        setOtpTestTool(null);
        setOtpTestPhone("");
      }
    } catch (e) {
      setResponse({ ok: false, data: { error: e.message } });
    } finally {
      setOtpTesting(false);
    }
  }

  const testableTools = tools.filter(t => t.type === "webhook" || t.type === "query");

  const TYPE_BADGE = { transferCall: ["Transfer", "warn"], webhook: ["Webhook", "default"], query: ["Query", "ok"] };

  return (
    <div>
      <div className="ph">
        <div>
          <h1 className="ph-title">Tools</h1>
          <div className="ph-subtitle">Webhook, transfer, and knowledge-base query tools that assistants can call mid-conversation.</div>
        </div>
        <div className="ph-actions">
          <Btn variant="primary" size="sm" icon="plus" onClick={() => { setNewTool({ ...BLANK_WEBHOOK }); setShowNew(true); }}>New tool</Btn>
        </div>
      </div>

      {showNew && (
        <ToolForm tool={newTool} setTool={setNewTool} onSave={saveTool} onCancel={() => { setShowNew(false); setNewTool({ ...BLANK_WEBHOOK }); }}
          isEdit={false} onUpload={uploadFile} onDeleteFile={deleteFile} />
      )}
      {editTool && (
        <ToolForm tool={editTool} setTool={setEditTool} onSave={saveTool} onCancel={() => setEditTool(null)}
          isEdit={true} onUpload={uploadFile} onDeleteFile={deleteFile} />
      )}
      {otpTestTool && (
        <div className="modal-back" onClick={() => !otpTesting && setOtpTestTool(null)}>
          <div className="modal" style={{ maxWidth: 460 }} onClick={e => e.stopPropagation()}>
            <div className="modal-head">
              <div className="card-title">Send test OTP</div>
              <Btn variant="ghost" size="sm" icon="x" disabled={otpTesting} onClick={() => setOtpTestTool(null)} />
            </div>
            <div className="modal-body">
              <Field label="Destination phone" help="Use Malaysia format such as 60123456789 or +60123456789. This sends a real SMS.">
                <Input value={otpTestPhone} onChange={e => setOtpTestPhone(e.target.value)} placeholder="60123456789" autoFocus />
              </Field>
            </div>
            <div className="modal-foot">
              <Btn variant="ghost" disabled={otpTesting} onClick={() => setOtpTestTool(null)}>Cancel</Btn>
              <Btn variant="primary" icon="send" disabled={otpTesting || !otpTestPhone.trim()} onClick={testOtpTool}>
                {otpTesting ? "Sending..." : "Send OTP"}
              </Btn>
            </div>
          </div>
        </div>
      )}

      <Card pad="none">
        {loading ? (
          <div className="empty-state"><div className="muted">Loading tools…</div></div>
        ) : (
          <div className="tbl-wrap">
            <table className="tbl">
              <thead>
                <tr><th>Tool</th><th>Type</th><th>Details</th><th>Status</th><th></th></tr>
              </thead>
              <tbody>
                {tools.map(t => {
                  const [badgeLabel, badgeTone] = TYPE_BADGE[t.type] || ["Unknown", "default"];
                  const iconName = t.type === "transferCall" ? "phone-outgoing" : t.type === "query" ? "database" : "tool";
                  const detail = t.type === "transferCall"
                    ? t.transferDestination || "—"
                    : t.type === "query"
                      ? (t.knowledgeBases || []).map(kb => `${kb.name} (${(kb.files||[]).length} file${(kb.files||[]).length !== 1 ? "s" : ""})`).join(", ") || "—"
                      : t.webhookUrl || "—";
                  return (
                    <tr key={t.id}>
                      <td>
                        <div className="row gap-3">
                          <div style={{ width: 30, height: 30, borderRadius: 7, background: "var(--c-surface)", border: "1px solid var(--c-line-2)", display: "grid", placeItems: "center" }}>
                            <Icon name={iconName} />
                          </div>
                          <div>
                            <div className="strong">{t.name}</div>
                            <div className="mono text-11 muted">{t.toolId || "—"}</div>
                          </div>
                        </div>
                      </td>
                      <td><Badge tone={badgeTone}>{badgeLabel}</Badge></td>
                      <td><span className="mono text-11 truncate" style={{ display: "inline-block", maxWidth: 260 }}>{detail}</span></td>
                      <td><Badge tone={t.enabled !== false ? "ok" : "default"} dot>{t.enabled !== false ? "enabled" : "disabled"}</Badge></td>
                      <td className="right">
                        <div className="row gap-1 justify-end">
                          {t.toolId === "send_otp_sms" && t.enabled !== false && (
                            <Btn variant="primary" size="sm" icon="send" onClick={() => { setOtpTestTool(t); setOtpTestPhone(""); }}>Test SMS</Btn>
                          )}
                          <Btn variant="ghost" size="sm" icon="edit-2" onClick={() => setEditTool({ ...t })} />
                          <Btn variant="ghost" size="sm" icon="trash-2" disabled={deleting === t.id} onClick={() => removeTool(t.id)} />
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
            {tools.length === 0 && (
              <div className="empty-state">
                <div className="empty-state-title">No tools yet</div>
                <div>Create a Webhook, Transfer Call, or Query (Knowledge Base) tool and assign it to an assistant.</div>
              </div>
            )}
          </div>
        )}
      </Card>

      {testableTools.length > 0 && (
        <div className="g-2 mt-4">
          <Card title="Tool playground" subtitle="Test a tool by sending a query or payload.">
            <div className="fld-row">
              <Field label="Tool">
                <Select value={playTool} onChange={e => {
                  setPlayTool(e.target.value);
                  const t = tools.find(x => x.id === e.target.value);
                  if (t?.type === "query") setPayload(`{\n  "query": "${(t.knowledgeBases||[{}])[0]?.name || 'DbName'} FIELD:value"\n}`);
                  else setPayload('{\n  "key": "value"\n}');
                }}>
                  <option value="">— select tool —</option>
                  {testableTools.map(t => <option key={t.id} value={t.id}>{t.name} ({t.type})</option>)}
                </Select>
              </Field>
            </div>
            <Field label="Payload / Query">
              <Textarea style={{ fontFamily: "var(--f-mono)", fontSize: 11.5, minHeight: 100 }} value={payload} onChange={e => setPayload(e.target.value)} />
            </Field>
            <Btn variant="primary" icon="play" disabled={!playTool || testing} onClick={testTool}>
              {testing ? "Running…" : "Run test"}
            </Btn>
          </Card>

          <Card title={response ? (response.ok ? "Result · OK" : "Result · Error") : "Result"}>
            {response ? (
              <pre className="code" style={{ fontSize: 11.5 }}><code>{typeof response.data === "string" ? response.data : JSON.stringify(response.data, null, 2)}</code></pre>
            ) : (
              <div className="empty-state"><div className="muted text-12">Run a test to see the result.</div></div>
            )}
          </Card>
        </div>
      )}
    </div>
  );
};

const AutomationsView = () => {
  const [automations, setAutomations] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [showNew, setShowNew] = React.useState(false);
  const [newAuto, setNewAuto] = React.useState({ name: "", webhookUrl: "", triggerEvent: "call.completed" });
  const [editAuto, setEditAuto] = React.useState(null);

  const EVENTS = ["call.completed", "call.escalated", "appointment.booked", "lead.captured", "payment.promised", "custom.event"];

  React.useEffect(() => {
    voxApiOptional("/automations/config", { automations: [] })
      .then(d => setAutomations(d.automations || []))
      .finally(() => setLoading(false));
  }, []);

  async function saveAuto(auto, isEdit) {
    if (!auto.name || !auto.webhookUrl) return;
    try {
      const saved = await voxApi("/automations/config", { method: "POST", body: JSON.stringify(auto) });
      const record = saved.automation || saved;
      if (isEdit) {
        setAutomations(a => a.map(x => x.id === record.id ? record : x));
        setEditAuto(null);
      } else {
        setAutomations(a => [...a, record]);
        setShowNew(false);
        setNewAuto({ name: "", webhookUrl: "", triggerEvent: "call.completed" });
      }
    } catch (e) {
      alert("Failed to save automation: " + e.message);
    }
  }

  async function toggleAuto(auto) {
    const updated = { ...auto, enabled: !auto.enabled };
    try {
      await voxApi("/automations/config", { method: "POST", body: JSON.stringify(updated) });
      setAutomations(a => a.map(x => x.id === auto.id ? updated : x));
    } catch {}
  }

  async function deleteAuto(id) {
    if (!confirm("Delete this automation?")) return;
    try {
      await voxApi(`/automations/config/${id}`, { method: "DELETE" });
      setAutomations(a => a.filter(x => x.id !== id));
    } catch (e) {
      alert("Failed to delete: " + e.message);
    }
  }

  return (
    <div>
      <div className="ph">
        <div>
          <h1 className="ph-title">Automations</h1>
          <div className="ph-subtitle">Forward VoxPro events to Make.com, n8n, Zapier, or any HTTP endpoint.</div>
        </div>
        <div className="ph-actions">
          <Btn variant="primary" size="sm" icon="plus" onClick={() => setShowNew(true)}>New automation</Btn>
        </div>
      </div>

      {(showNew || editAuto) && (
        <div className="modal-back" onClick={() => { setShowNew(false); setEditAuto(null); }}>
          <div className="modal" onClick={e => e.stopPropagation()}>
            <div className="modal-head">
              <div className="card-title">{editAuto ? "Edit automation" : "New automation"}</div>
              <Btn variant="ghost" size="sm" icon="x" onClick={() => { setShowNew(false); setEditAuto(null); }} />
            </div>
            <div className="modal-body">
              {editAuto ? (<>
                <Field label="Automation name"><Input value={editAuto.name} onChange={e => setEditAuto(d => ({ ...d, name: e.target.value }))} placeholder="e.g. CRM sync on call end" /></Field>
                <Field label="Webhook URL"><Input value={editAuto.webhookUrl} onChange={e => setEditAuto(d => ({ ...d, webhookUrl: e.target.value }))} placeholder="https://hook.make.com/…" /></Field>
                <Field label="Trigger event">
                  <Select value={editAuto.triggerEvent} onChange={e => setEditAuto(d => ({ ...d, triggerEvent: e.target.value }))}>
                    {EVENTS.map(ev => <option key={ev} value={ev}>{ev}</option>)}
                  </Select>
                </Field>
              </>) : (<>
                <Field label="Automation name"><Input value={newAuto.name} onChange={e => setNewAuto(d => ({ ...d, name: e.target.value }))} placeholder="e.g. CRM sync on call end" /></Field>
                <Field label="Webhook URL"><Input value={newAuto.webhookUrl} onChange={e => setNewAuto(d => ({ ...d, webhookUrl: e.target.value }))} placeholder="https://hook.make.com/…" /></Field>
                <Field label="Trigger event">
                  <Select value={newAuto.triggerEvent} onChange={e => setNewAuto(d => ({ ...d, triggerEvent: e.target.value }))}>
                    {EVENTS.map(ev => <option key={ev} value={ev}>{ev}</option>)}
                  </Select>
                </Field>
              </>)}
            </div>
            <div className="modal-foot">
              <Btn variant="ghost" onClick={() => { setShowNew(false); setEditAuto(null); }}>Cancel</Btn>
              {editAuto
                ? <Btn variant="primary" onClick={() => saveAuto(editAuto, true)} disabled={!editAuto.name || !editAuto.webhookUrl}>Save changes</Btn>
                : <Btn variant="primary" onClick={() => saveAuto(newAuto, false)} disabled={!newAuto.name || !newAuto.webhookUrl}>Save</Btn>
              }
            </div>
          </div>
        </div>
      )}

      {loading ? (
        <Card><div className="empty-state"><div className="muted">Loading automations…</div></div></Card>
      ) : automations.length === 0 ? (
        <Card>
          <div className="empty-state">
            <div className="empty-state-title">No automations yet</div>
            <div>Create an automation to forward call events to Make.com, Zapier, or any webhook.</div>
          </div>
        </Card>
      ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {automations.map(a => (
            <Card key={a.id} pad="lg">
              <div className="row" style={{ alignItems: "flex-start" }}>
                <div className="row gap-3" style={{ flex: 1 }}>
                  <div style={{ width: 36, height: 36, borderRadius: 9, background: "var(--c-surface)", border: "1px solid var(--c-line-2)", display: "grid", placeItems: "center" }}>
                    <Icon name="zap" />
                  </div>
                  <div>
                    <div className="row gap-2">
                      <span className="strong text-14">{a.name}</span>
                      <Badge tone={a.enabled ? "ok" : "default"} dot>{a.enabled ? "live" : "paused"}</Badge>
                    </div>
                    <div className="muted text-12 mt-1">{a.description || a.webhookUrl}</div>
                    <div className="row gap-2 mt-2">
                      <span className="chip"><span className="mono">{a.triggerEvent}</span></span>
                    </div>
                  </div>
                </div>
                <Toggle checked={!!a.enabled} onChange={() => toggleAuto(a)} />
              </div>

              <div className="hr" />

              <div className="row gap-4" style={{ justifyContent: "space-between" }}>
                <div><div className="text-11 muted">Endpoint</div><div className="mono text-11 mt-1 truncate" style={{ maxWidth: 260 }}>{a.webhookUrl}</div></div>
                <div className="row gap-2">
                  <Btn variant="ghost" size="sm" icon="edit" onClick={() => setEditAuto({ ...a })}>Edit</Btn>
                  <Btn variant="ghost" size="sm" icon="trash" style={{ color: "var(--err)" }} onClick={() => deleteAuto(a.id)} />
                </div>
              </div>
            </Card>
          ))}
        </div>
      )}

      <Card title="Trigger from your own backend" style={{ marginTop: 12 }} subtitle="POST to /automations/trigger with a matching triggerEvent.">
        <pre className="code"><code>{`curl -X POST /automations/trigger \\
  -H 'Content-Type: application/json' \\
  -d '{
    "triggerEvent": "lead.captured",
    "payload": {
      "contactId": "contact_001",
      "transcript": "Customer asked for a quote",
      "outcome": "qualified"
    }
  }'`}</code></pre>
      </Card>
    </div>
  );
};

const AsrProviderCard = () => {
  const [config, setConfig] = React.useState({ provider: "ytl", deepgramApiKey: "", deepgramModel: "nova-2", language: "ms" });
  const [saving, setSaving] = React.useState(false);
  const [saved, setSaved] = React.useState(false);
  const [err, setErr] = React.useState("");

  React.useEffect(() => {
    voxApiOptional("/asr/config", {}).then(d => {
      if (d.config) setConfig(c => ({ ...c, ...d.config, deepgramApiKey: "" }));
    });
  }, []);

  async function save() {
    setSaving(true); setErr("");
    try {
      await voxApi("/asr/config", { method: "POST", body: JSON.stringify(config) });
      setSaved(true);
      setTimeout(() => setSaved(false), 2000);
    } catch (e) {
      setErr(e.message || "Save failed");
    } finally {
      setSaving(false);
    }
  }

  return (
    <Card title="Speech Recognition (ASR)">
      <Field label="Provider">
        <div className="row gap-2">
          {[{ v: "ytl", label: "YTL ILMU" }, { v: "deepgram", label: "Deepgram" }].map(opt => (
            <button key={opt.v} type="button"
              onClick={() => setConfig(c => ({ ...c, provider: opt.v }))}
              style={{
                flex: 1, padding: "8px 10px", borderRadius: 8, cursor: "pointer", fontSize: 12, fontWeight: 500, transition: "all .15s",
                background: config.provider === opt.v ? "var(--a)" : "var(--c-surface)",
                color: config.provider === opt.v ? "#fff" : "var(--c-fg)",
                border: config.provider === opt.v ? "1.5px solid var(--a)" : "1.5px solid var(--c-line-2)"
              }}>
              {opt.label}
            </button>
          ))}
        </div>
      </Field>

      {config.provider === "deepgram" && (
        <>
          <Field label="Deepgram API key" help="Get a free key at deepgram.com — 200 hrs/month free">
            <Input type="password" value={config.deepgramApiKey} placeholder="Leave blank to keep existing key"
              onChange={e => setConfig(c => ({ ...c, deepgramApiKey: e.target.value }))} />
          </Field>
          <div className="fld-row">
            <Field label="Model">
              <Select value={config.deepgramModel || "nova-2"} onChange={e => setConfig(c => ({ ...c, deepgramModel: e.target.value }))}>
                <option value="nova-2">nova-2 (recommended)</option>
                <option value="nova">nova</option>
                <option value="enhanced">enhanced</option>
                <option value="base">base</option>
              </Select>
            </Field>
            <Field label="Language">
              <Select value={config.language || "ms"} onChange={e => setConfig(c => ({ ...c, language: e.target.value }))}>
                <option value="ms">Bahasa Malaysia (ms)</option>
                <option value="en">English (en)</option>
                <option value="id">Indonesian (id)</option>
              </Select>
            </Field>
          </div>
        </>
      )}

      {err && <div style={{ fontSize: 12, color: "var(--err)", marginBottom: 6 }}>{err}</div>}

      <Btn variant="primary" size="sm" icon="check" disabled={saving} onClick={save}>
        {saved ? "Saved!" : saving ? "Saving…" : "Save ASR settings"}
      </Btn>
    </Card>
  );
};

// ─── ASR Corrections View ────────────────────────────────────────────────────
const AsrCorrectionsView = () => {
  const [corrections, setCorrections] = React.useState({});
  const [suggestions, setSuggestions] = React.useState([]);
  const [loading, setLoading]         = React.useState(true);
  const [filter, setFilter]           = React.useState("pending");
  const [newWrong, setNewWrong]       = React.useState("");
  const [newRight, setNewRight]       = React.useState("");
  const [adding, setAdding]           = React.useState(false);
  const [copied, setCopied]           = React.useState(null);
  const [acting, setActing]           = React.useState(null);

  async function load() {
    setLoading(true);
    try {
      const [c, s] = await Promise.all([
        voxApiOptional("/asr/corrections", { corrections: {} }),
        voxApiOptional("/asr/suggestions", { suggestions: [] }),
      ]);
      setCorrections(c.corrections || {});
      setSuggestions(s.suggestions || []);
    } finally {
      setLoading(false);
    }
  }

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

  async function approve(id) {
    setActing(id + "-approve");
    try {
      const d = await voxApi(`/asr/suggestions/${id}/approve`, { method: "POST" });
      setCorrections(d.corrections || {});
      setSuggestions(prev => prev.map(s => s.id === id ? { ...s, status: "approved" } : s));
    } finally { setActing(null); }
  }

  async function reject(id) {
    setActing(id + "-reject");
    try {
      await voxApi(`/asr/suggestions/${id}/reject`, { method: "POST" });
      setSuggestions(prev => prev.map(s => s.id === id ? { ...s, status: "rejected" } : s));
    } finally { setActing(null); }
  }

  async function removeSuggestion(id) {
    await voxApi(`/asr/suggestions/${id}`, { method: "DELETE" });
    setSuggestions(prev => prev.filter(s => s.id !== id));
  }

  async function removeCorrection(wrong) {
    await voxApi(`/asr/corrections/${encodeURIComponent(wrong)}`, { method: "DELETE" });
    setCorrections(prev => { const n = { ...prev }; delete n[wrong]; return n; });
  }

  async function addManual() {
    if (!newWrong.trim() || !newRight.trim()) return;
    setAdding(true);
    try {
      const d = await voxApi("/asr/corrections", {
        method: "POST",
        body: JSON.stringify({ wrong: newWrong.trim(), right: newRight.trim() }),
      });
      setCorrections(d.corrections || {});
      setNewWrong(""); setNewRight("");
    } finally { setAdding(false); }
  }

  function copy(text, key) {
    navigator.clipboard.writeText(text).catch(() => {});
    setCopied(key);
    setTimeout(() => setCopied(null), 1800);
  }

  const correctionEntries = Object.entries(corrections);
  const pendingCount  = suggestions.filter(s => s.status === "pending").length;
  const approvedCount = suggestions.filter(s => s.status === "approved").length;
  const visibleSugg   = suggestions.filter(s => filter === "all" ? true : s.status === filter)
    .sort((a, b) => new Date(b.suggestedAt) - new Date(a.suggestedAt));

  return (
    <div className="view-stack">
      {/* Header */}
      <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-start", marginBottom: 20, flexWrap: "wrap", gap: 12 }}>
        <div>
          <div className="strong" style={{ fontSize: 18 }}>ASR Corrections</div>
          <div className="muted text-12 mt-1">Review AI-suggested mishearing fixes. Approve to make them live instantly.</div>
        </div>
        <div className="row gap-2">
          {pendingCount > 0 && (
            <Badge style={{ background: "#fef3c7", color: "#92400e", border: "1px solid #fde68a" }}>
              {pendingCount} pending
            </Badge>
          )}
          <Btn variant="ghost" size="sm" icon="refresh-cw" onClick={load}>Refresh</Btn>
        </div>
      </div>

      {/* Stats */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 12, marginBottom: 20 }}>
        {[
          { label: "Active corrections", value: correctionEntries.length, color: "var(--ok)" },
          { label: "Pending review",     value: pendingCount,             color: "#f59e0b" },
          { label: "Approved (all-time)",value: approvedCount,            color: "var(--c-fg)" },
          { label: "Rejected",           value: suggestions.filter(s => s.status === "rejected").length, color: "var(--c-text-3)" },
        ].map(s => (
          <div key={s.label} style={{ background: "var(--c-surface)", border: "1px solid var(--c-line-2)", borderRadius: 10, padding: "14px 16px" }}>
            <div style={{ fontSize: 22, fontWeight: 700, color: s.color }}>{s.value}</div>
            <div className="muted text-11 mt-1">{s.label}</div>
          </div>
        ))}
      </div>

      {/* Suggestions section */}
      <Card title="Suggested Corrections" subtitle="From n8n workflow 17 — AI-detected mishearings needing your sign-off">
        <div className="row gap-2" style={{ marginBottom: 12 }}>
          {[["pending","Pending"], ["approved","Approved"], ["rejected","Rejected"], ["all","All"]].map(([v, l]) => (
            <button key={v} type="button" onClick={() => setFilter(v)}
              style={{
                padding: "4px 12px", borderRadius: 20, fontSize: 12, fontWeight: 500, cursor: "pointer", border: "none",
                background: filter === v ? "var(--a)" : "var(--c-surface-2)",
                color: filter === v ? "#fff" : "var(--c-text-2)",
                outline: filter === v ? "none" : "1px solid var(--c-line-2)",
              }}>
              {l}
            </button>
          ))}
        </div>

        {loading ? (
          <div className="muted text-12" style={{ padding: "24px 0", textAlign: "center" }}>Loading…</div>
        ) : visibleSugg.length === 0 ? (
          <div style={{ textAlign: "center", padding: "32px 0" }}>
            <div style={{ fontSize: 28, marginBottom: 10 }}>
              {filter === "pending" ? "✅" : "📭"}
            </div>
            <div className="muted text-13">
              {filter === "pending" ? "No pending suggestions — the queue is clear." : "Nothing here."}
            </div>
          </div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {visibleSugg.map(s => (
              <div key={s.id} style={{
                border: "1px solid var(--c-line-2)", borderRadius: 10, padding: "12px 16px",
                background: s.status === "approved" ? "#f0fdf418" : s.status === "rejected" ? "#fff1f218" : "var(--c-surface)",
                borderLeft: `3px solid ${s.status === "approved" ? "var(--ok)" : s.status === "rejected" ? "var(--err)" : "#f59e0b"}`,
              }}>
                {/* Wrong → Right */}
                <div className="row gap-2" style={{ alignItems: "center", marginBottom: 8, flexWrap: "wrap" }}>
                  <code style={{ background: "#fef3c7", color: "#92400e", padding: "2px 8px", borderRadius: 5, fontSize: 13, fontFamily: "monospace" }}>
                    {s.wrong}
                  </code>
                  <span className="muted" style={{ fontSize: 14 }}>→</span>
                  <code style={{ background: "#f0fdf4", color: "#166534", padding: "2px 8px", borderRadius: 5, fontSize: 13, fontFamily: "monospace" }}>
                    {s.right}
                  </code>
                  {s.count > 1 && (
                    <Badge style={{ fontSize: 10, background: "#fef3c7", color: "#92400e" }}>{s.count}× seen</Badge>
                  )}
                  <span style={{ marginLeft: "auto", fontSize: 11, color: "var(--c-text-3)" }}>
                    {s.confidence} confidence
                  </span>
                </div>

                {/* Reason */}
                {s.reason && <div className="muted text-12" style={{ marginBottom: 6 }}>{s.reason}</div>}

                {/* Transcript excerpt */}
                {s.transcriptExcerpt && (
                  <div style={{ background: "var(--c-bg)", border: "1px solid var(--c-line)", borderRadius: 6, padding: "6px 10px", fontSize: 12, color: "var(--c-text-2)", marginBottom: 10, fontStyle: "italic" }}>
                    "…{s.transcriptExcerpt}…"
                  </div>
                )}

                {/* Actions */}
                <div className="row gap-2">
                  {s.status === "pending" && (
                    <>
                      <Btn variant="primary" size="sm" icon="check"
                        disabled={acting === s.id + "-approve"}
                        onClick={() => approve(s.id)}>
                        {acting === s.id + "-approve" ? "Approving…" : "Approve"}
                      </Btn>
                      <Btn variant="ghost" size="sm" icon="x"
                        disabled={acting === s.id + "-reject"}
                        onClick={() => reject(s.id)}>
                        Reject
                      </Btn>
                    </>
                  )}
                  {s.status !== "pending" && (
                    <Badge style={{
                      background: s.status === "approved" ? "#dcfce7" : "#fee2e2",
                      color: s.status === "approved" ? "#166534" : "#991b1b",
                    }}>
                      {s.status}
                    </Badge>
                  )}
                  <Btn variant="ghost" size="sm" icon="trash-2"
                    style={{ marginLeft: "auto", color: "var(--err)" }}
                    onClick={() => removeSuggestion(s.id)}>
                  </Btn>
                </div>
              </div>
            ))}
          </div>
        )}
      </Card>

      {/* Active corrections dictionary */}
      <Card title="Active Corrections" subtitle={`${correctionEntries.length} rules applied to every call transcript in real time`}>
        {/* Add manually */}
        <div className="fld-row" style={{ marginBottom: 14 }}>
          <Field label="Misheard word">
            <Input value={newWrong} onChange={e => setNewWrong(e.target.value)}
              placeholder="e.g. beason" onKeyDown={e => e.key === "Enter" && addManual()} />
          </Field>
          <Field label="Correct word / phrase">
            <Input value={newRight} onChange={e => setNewRight(e.target.value)}
              placeholder="e.g. BSN" onKeyDown={e => e.key === "Enter" && addManual()} />
          </Field>
          <div style={{ paddingTop: 22 }}>
            <Btn variant="primary" size="sm" icon="plus" disabled={adding || !newWrong.trim() || !newRight.trim()} onClick={addManual}>
              Add
            </Btn>
          </div>
        </div>

        {correctionEntries.length === 0 ? (
          <div className="muted text-12" style={{ textAlign: "center", padding: "20px 0" }}>
            No custom corrections yet — approve a suggestion above or add one manually.
          </div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {correctionEntries.map(([wrong, right]) => (
              <div key={wrong} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 12px", background: "var(--c-surface)", border: "1px solid var(--c-line-2)", borderRadius: 8 }}>
                <code style={{ color: "#92400e", background: "#fef3c7", padding: "2px 7px", borderRadius: 4, fontSize: 12, fontFamily: "monospace" }}>
                  {wrong}
                </code>
                <span className="muted" style={{ fontSize: 13 }}>→</span>
                <code style={{ color: "#166534", background: "#f0fdf4", padding: "2px 7px", borderRadius: 4, fontSize: 12, fontFamily: "monospace", flex: 1 }}>
                  {right}
                </code>
                <button
                  onClick={() => copy(`${wrong} → ${right}`, wrong)}
                  style={{ background: "none", border: "none", cursor: "pointer", color: "var(--c-text-3)", fontSize: 11, padding: "2px 6px" }}>
                  {copied === wrong ? "✓" : "copy"}
                </button>
                <Btn variant="ghost" size="sm" icon="trash-2"
                  style={{ color: "var(--err)" }}
                  onClick={() => { if (confirm(`Remove "${wrong}" → "${right}"?`)) removeCorrection(wrong); }} />
              </div>
            ))}
          </div>
        )}
      </Card>

      {/* n8n setup hint */}
      <Card title="n8n Setup" subtitle="Point workflow 17 at this endpoint instead of Slack">
        <div className="muted text-12" style={{ lineHeight: 1.6, marginBottom: 12 }}>
          In n8n workflow 17 (ASR Correction Suggester), change the action node to POST to:
        </div>
        <div style={{ background: "var(--c-bg)", border: "1px solid var(--c-line-2)", borderRadius: 8, padding: "10px 14px", fontFamily: "monospace", fontSize: 12, position: "relative" }}>
          POST {window.location.origin}/asr/suggestions
          <button
            onClick={() => copy(`${window.location.origin}/asr/suggestions`, "asr-url")}
            style={{ position: "absolute", top: 8, right: 8, background: "none", border: "none", cursor: "pointer", color: "var(--c-text-3)", fontSize: 11 }}>
            {copied === "asr-url" ? "✓" : "copy"}
          </button>
        </div>
        <div className="muted text-12 mt-2">
          Payload: <code style={{ fontSize: 11 }}>{"{ wrong, right, confidence, reason, phone, transcriptExcerpt }"}</code>
        </div>
      </Card>
    </div>
  );
};

const SystemView = ({ envConfig = {} }) => {
  const [saved, setSaved] = React.useState(false);
  const [webhookUrl, setWebhookUrl] = React.useState("https://api.xandrix.my/voxpro/events");
  const [wsName, setWsName] = React.useState("Xandrix");
  const [lang, setLang] = React.useState("ms");
  const [tz, setTz] = React.useState("Asia/Kuala_Lumpur");

  function saveSettings() {
    setSaved(true);
    setTimeout(() => setSaved(false), 2000);
  }

  const envVars = [
    { key: "LIVEKIT_URL", val: envConfig.LIVEKIT_URL || "wss://…" },
    { key: "YTL_LLM_MODEL", val: envConfig.YTL_LLM_MODEL || "ILMU-text" },
    { key: "YTL_ASR_MODEL", val: envConfig.YTL_ASR_MODEL || "ILMU-asr" },
    { key: "TTS_PROVIDER", val: envConfig.TTS_PROVIDER || "openai" },
    { key: "VOICE_LANGUAGE", val: envConfig.VOICE_LANGUAGE || "en" },
    { key: "PORT", val: envConfig.PORT || "3000" },
  ];

  return (
    <div>
      <div className="ph">
        <div>
          <h1 className="ph-title">System</h1>
          <div className="ph-subtitle">Workspace settings, environment, API keys, audit log.</div>
        </div>
        <div className="ph-actions">
          <Btn variant="primary" size="sm" icon="check" onClick={saveSettings}>{saved ? "Saved!" : "Save"}</Btn>
        </div>
      </div>

      <div className="g-2-1">
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <Card title="API keys">
            <div className="col">
              {[
                { name: "Production · server", key: "vxp_live_••••••••a47c", last: "2m ago" },
                { name: "Staging", key: "vxp_test_••••••••88e2", last: "1d ago" },
                { name: "CI · webhook", key: "vxp_live_••••••••2f01", last: "—" },
              ].map(k => (
                <div key={k.name} className="row gap-3" style={{ padding: "12px 0", borderBottom: "1px solid var(--c-line)" }}>
                  <div style={{ flex: 1 }}>
                    <div className="strong text-12">{k.name}</div>
                    <div className="mono text-11 muted">{k.key}</div>
                  </div>
                  <div className="muted text-11" style={{ width: 110, textAlign: "right" }}>Last used {k.last}</div>
                  <Btn variant="ghost" size="sm" icon="copy" />
                  <Btn variant="ghost" size="sm" icon="trash" style={{ color: "var(--err)" }} />
                </div>
              ))}
            </div>
            <Btn variant="primary" icon="plus" size="sm" style={{ marginTop: 14 }}>Generate key</Btn>
          </Card>

          <Card title="Environment" subtitle="Active provider configuration">
            <div className="g-2">
              {envVars.map(ev => (
                <div key={ev.key}>
                  <div className="text-11 muted">{ev.key}</div>
                  <div className="mono text-12 mt-1">{ev.val}</div>
                </div>
              ))}
            </div>
          </Card>

          <AsrProviderCard />

          <Card title="Webhooks">
            <Field label="Default webhook URL"><Input value={webhookUrl} onChange={e => setWebhookUrl(e.target.value)} /></Field>
            <Field label="Signing secret"><Input defaultValue="vxp_whsec_••••••••••••" /></Field>
            <Field label="Retry policy">
              <Select defaultValue="exp">
                <option value="exp">Exponential backoff · 5 attempts</option>
                <option value="linear">Linear · 3 attempts</option>
                <option value="off">No retries</option>
              </Select>
            </Field>
          </Card>
        </div>

        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <Card title="Members">
            <div className="col">
              {[
                { name: "Admin", email: "admin@voxpro.my", role: "Owner", initials: "VP" },
              ].map(m => (
                <div key={m.email} className="row gap-3" style={{ padding: "10px 0", borderBottom: "1px solid var(--c-line)" }}>
                  <div className="tb-avatar" style={{ width: 28, height: 28, fontSize: 10 }}>{m.initials}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div className="strong text-12">{m.name}</div>
                    <div className="muted text-11 truncate">{m.email}</div>
                  </div>
                  <Badge>{m.role}</Badge>
                </div>
              ))}
            </div>
            <Btn variant="primary" icon="plus" size="sm" style={{ marginTop: 12 }}>Invite member</Btn>
          </Card>

          <Card title="Audit log" subtitle="Recent activity">
            <div style={{ display: "flex", flexDirection: "column", gap: 12, fontSize: 12 }}>
              {[
                { t: "now", who: "System", what: "started", item: "VoxPro backend" },
                { t: "—", who: "Admin", what: "loaded", item: "Console" },
              ].map((a, i) => (
                <div key={i} className="row gap-3" style={{ alignItems: "flex-start" }}>
                  <div className="mono text-11 muted" style={{ width: 38, flex: "none" }}>{a.t}</div>
                  <div style={{ flex: 1 }}>
                    <span className="strong">{a.who}</span> <span className="muted">{a.what}</span> {a.item}
                  </div>
                </div>
              ))}
            </div>
          </Card>

          <Card title="Workspace">
            <Field label="Workspace name"><Input value={wsName} onChange={e => setWsName(e.target.value)} /></Field>
            <Field label="Default language">
              <Select value={lang} onChange={e => setLang(e.target.value)}>
                <option value="ms">Bahasa Malaysia</option>
                <option value="en">English</option>
              </Select>
            </Field>
            <Field label="Timezone">
              <Select value={tz} onChange={e => setTz(e.target.value)}>
                <option value="Asia/Kuala_Lumpur">Asia/Kuala_Lumpur</option>
                <option value="Asia/Singapore">Asia/Singapore</option>
                <option value="Asia/Jakarta">Asia/Jakarta</option>
              </Select>
            </Field>
            <div className="row mt-3" style={{ justifyContent: "space-between" }}>
              <div>
                <div className="strong text-12">Danger zone</div>
                <div className="muted text-11 mt-1">Delete workspace and all data permanently.</div>
              </div>
              <Btn variant="ghost" size="sm" style={{ color: "var(--err)" }}>Delete workspace</Btn>
            </div>
          </Card>
        </div>
      </div>
    </div>
  );
};

// ─── KB Gap View ──────────────────────────────────────────────────────────────
const KbGapsView = () => {
  const [gaps, setGaps]           = React.useState([]);
  const [config, setConfig]       = React.useState({ teamsWebhookUrl: "", alertThreshold: 3 });
  const [filter, setFilter]       = React.useState("open");
  const [loading, setLoading]     = React.useState(true);
  const [saving, setSaving]       = React.useState(false);
  const [saveDone, setSaveDone]   = React.useState(false);
  const [expanded, setExpanded]   = React.useState(null);
  const [copied, setCopied]       = React.useState(null);

  async function load() {
    setLoading(true);
    try {
      const d = await voxApiOptional("/kb-gaps", { gaps: [], teamsWebhookUrl: "", alertThreshold: 3 });
      setGaps(d.gaps || []);
      setConfig({ teamsWebhookUrl: d.teamsWebhookUrl || "", alertThreshold: d.alertThreshold || 3 });
    } finally {
      setLoading(false);
    }
  }

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

  async function resolve(id) {
    await voxApi(`/kb-gaps/${id}/resolve`, { method: "PATCH" });
    setGaps(prev => prev.map(g => g.id === id ? { ...g, status: "resolved" } : g));
  }

  async function remove(id) {
    await voxApi(`/kb-gaps/${id}`, { method: "DELETE" });
    setGaps(prev => prev.filter(g => g.id !== id));
  }

  async function saveConfig() {
    setSaving(true);
    try {
      await voxApi("/kb-gaps/config", { method: "POST", body: JSON.stringify(config) });
      setSaveDone(true);
      setTimeout(() => setSaveDone(false), 2000);
    } finally {
      setSaving(false);
    }
  }

  function copyText(text, id) {
    navigator.clipboard.writeText(text).catch(() => {});
    setCopied(id);
    setTimeout(() => setCopied(null), 1800);
  }

  const visible = gaps.filter(g => filter === "all" ? true : g.status === filter)
    .sort((a, b) => b.count - a.count);

  const openCount = gaps.filter(g => g.status === "open").length;
  const alertCount = gaps.filter(g => g.status === "open" && g.count >= config.alertThreshold).length;

  function countColor(n) {
    if (n >= config.alertThreshold) return "var(--err)";
    if (n === config.alertThreshold - 1) return "#f59e0b";
    return "var(--ok)";
  }

  return (
    <div className="view-stack">
      {/* Header */}
      <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-start", marginBottom: 20, flexWrap: "wrap", gap: 12 }}>
        <div>
          <div className="strong" style={{ fontSize: 18 }}>KB Gap Detector</div>
          <div className="muted text-12 mt-1">Questions the AI fumbled — reviewed here, fixed in the knowledge base.</div>
        </div>
        <div className="row gap-2">
          {alertCount > 0 && (
            <Badge style={{ background: "#fef2f2", color: "var(--err)", border: "1px solid #fecaca" }}>
              {alertCount} need attention
            </Badge>
          )}
          <Btn variant="ghost" size="sm" icon="refresh-cw" onClick={load}>Refresh</Btn>
        </div>
      </div>

      {/* Stats row */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 12, marginBottom: 20 }}>
        {[
          { label: "Total gaps", value: gaps.length, color: "var(--c-fg)" },
          { label: "Open", value: openCount, color: "#f59e0b" },
          { label: `≥ ${config.alertThreshold}× (alert)`, value: alertCount, color: "var(--err)" },
          { label: "Resolved", value: gaps.filter(g => g.status === "resolved").length, color: "var(--ok)" },
        ].map(s => (
          <div key={s.label} style={{ background: "var(--c-surface)", border: "1px solid var(--c-line-2)", borderRadius: 10, padding: "14px 16px" }}>
            <div style={{ fontSize: 22, fontWeight: 700, color: s.color }}>{s.value}</div>
            <div className="muted text-11 mt-1">{s.label}</div>
          </div>
        ))}
      </div>

      {/* Config card */}
      <Card title="Teams Notification" subtitle="Fires when a gap reaches the alert threshold">
        <div className="fld-row">
          <Field label="Microsoft Teams Incoming Webhook URL" help="In Teams: channel → Connectors → Incoming Webhook → create → copy URL">
            <Input
              value={config.teamsWebhookUrl}
              onChange={e => setConfig(c => ({ ...c, teamsWebhookUrl: e.target.value }))}
              placeholder="https://yourtenant.webhook.office.com/webhookb2/…"
            />
          </Field>
          <Field label="Alert threshold (times asked)" style={{ flex: "0 0 180px" }}>
            <Input
              type="number"
              min="1" max="20"
              value={config.alertThreshold}
              onChange={e => setConfig(c => ({ ...c, alertThreshold: Number(e.target.value) || 3 }))}
            />
          </Field>
        </div>
        <Btn variant="primary" size="sm" icon="check" disabled={saving} onClick={saveConfig}>
          {saveDone ? "Saved!" : saving ? "Saving…" : "Save settings"}
        </Btn>
      </Card>

      {/* Filter tabs */}
      <div className="row gap-2" style={{ margin: "16px 0 8px" }}>
        {[["open", "Open"], ["resolved", "Resolved"], ["all", "All"]].map(([v, l]) => (
          <button key={v} type="button" onClick={() => setFilter(v)}
            style={{
              padding: "5px 14px", borderRadius: 20, fontSize: 12, fontWeight: 500, cursor: "pointer", border: "none",
              background: filter === v ? "var(--a)" : "var(--c-surface)",
              color: filter === v ? "#fff" : "var(--c-text-2)",
              outline: filter === v ? "none" : "1px solid var(--c-line-2)",
            }}>
            {l}
          </button>
        ))}
        <span className="muted text-11" style={{ marginLeft: 8 }}>{visible.length} gap{visible.length !== 1 ? "s" : ""}</span>
      </div>

      {/* Gaps list */}
      {loading ? (
        <div className="muted text-13" style={{ padding: "32px 0", textAlign: "center" }}>Loading…</div>
      ) : visible.length === 0 ? (
        <div style={{ textAlign: "center", padding: "48px 0" }}>
          <div style={{ fontSize: 32, marginBottom: 12 }}>🎉</div>
          <div className="strong text-14">No {filter === "all" ? "" : filter} gaps</div>
          <div className="muted text-12 mt-1">{filter === "open" ? "All questions are being answered well." : "Nothing here yet."}</div>
        </div>
      ) : (
        <Card pad="none">
          {visible.map((g, i) => (
            <div key={g.id} style={{ borderBottom: i < visible.length - 1 ? "1px solid var(--c-line)" : "none" }}>
              {/* Row header */}
              <div
                style={{ display: "flex", alignItems: "flex-start", gap: 12, padding: "14px 18px", cursor: "pointer" }}
                onClick={() => setExpanded(expanded === g.id ? null : g.id)}
              >
                {/* Count badge */}
                <div style={{
                  minWidth: 36, height: 36, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center",
                  background: `${countColor(g.count)}18`, color: countColor(g.count),
                  fontSize: 15, fontWeight: 700, flex: "none",
                }}>
                  {g.count}×
                </div>

                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="strong text-13" style={{ marginBottom: 3, lineHeight: 1.3 }}>{g.question}</div>
                  <div className="row gap-3 text-11 muted" style={{ flexWrap: "wrap" }}>
                    <span>🤖 {g.assistantName || g.assistantId || "Unknown"}</span>
                    <span>📋 {g.responseQuality}</span>
                    <span>🕐 {g.lastSeen ? new Date(g.lastSeen).toLocaleDateString("en-MY") : "—"}</span>
                    {g.status === "resolved" && <span style={{ color: "var(--ok)" }}>✓ resolved</span>}
                    {g.count >= config.alertThreshold && g.status === "open" && (
                      <span style={{ color: "var(--err)", fontWeight: 600 }}>⚠ alert threshold reached</span>
                    )}
                  </div>
                </div>

                <Icon name={expanded === g.id ? "chevron-up" : "chevron-down"}
                  style={{ width: 14, height: 14, color: "var(--c-text-3)", flex: "none", marginTop: 2 }} />
              </div>

              {/* Expanded detail */}
              {expanded === g.id && (
                <div style={{ background: "var(--c-surface)", borderTop: "1px solid var(--c-line)", padding: "14px 18px 16px", display: "flex", flexDirection: "column", gap: 12 }}>
                  {g.suggestedAnswer && (
                    <div>
                      <div className="text-11 muted" style={{ marginBottom: 5 }}>Suggested KB answer</div>
                      <div style={{ background: "var(--c-bg)", border: "1px solid var(--c-line-2)", borderRadius: 8, padding: "10px 14px", fontSize: 13, lineHeight: 1.6, position: "relative" }}>
                        {g.suggestedAnswer}
                        <button
                          onClick={() => copyText(g.suggestedAnswer, g.id + "-ans")}
                          style={{ position: "absolute", top: 8, right: 8, background: "none", border: "none", cursor: "pointer", color: "var(--c-text-3)", fontSize: 11 }}
                        >
                          {copied === g.id + "-ans" ? "✓ copied" : "copy"}
                        </button>
                      </div>
                    </div>
                  )}
                  {g.callerPhones?.length > 0 && (
                    <div>
                      <div className="text-11 muted" style={{ marginBottom: 4 }}>Callers who asked this</div>
                      <div className="row gap-2" style={{ flexWrap: "wrap" }}>
                        {g.callerPhones.slice(0, 8).map(p => (
                          <Badge key={p} style={{ fontFamily: "monospace", fontSize: 11 }}>{p}</Badge>
                        ))}
                        {g.callerPhones.length > 8 && <Badge>+{g.callerPhones.length - 8} more</Badge>}
                      </div>
                    </div>
                  )}
                  <div className="row gap-2 mt-1">
                    {g.status === "open" && (
                      <Btn variant="primary" size="sm" icon="check" onClick={() => resolve(g.id)}>
                        Mark resolved
                      </Btn>
                    )}
                    <Btn variant="ghost" size="sm" icon="copy"
                      onClick={() => copyText(g.question, g.id + "-q")}>
                      {copied === g.id + "-q" ? "Copied!" : "Copy question"}
                    </Btn>
                    <Btn variant="ghost" size="sm" icon="trash-2"
                      style={{ color: "var(--err)", marginLeft: "auto" }}
                      onClick={() => { if (confirm("Delete this gap entry?")) remove(g.id); }}>
                      Delete
                    </Btn>
                  </div>
                </div>
              )}
            </div>
          ))}
        </Card>
      )}

      {/* n8n setup hint */}
      <Card title="n8n Setup" subtitle="Wire the KB gap detector workflow to this panel">
        <div className="muted text-12" style={{ lineHeight: 1.6, marginBottom: 12 }}>
          In n8n workflow 18 (KB Gap Detector), set the <strong>POST URL</strong> to:
        </div>
        <div style={{ background: "var(--c-bg)", border: "1px solid var(--c-line-2)", borderRadius: 8, padding: "10px 14px", fontFamily: "monospace", fontSize: 12, position: "relative" }}>
          POST {window.location.origin}/kb-gaps
          <button
            onClick={() => copyText(`${window.location.origin}/kb-gaps`, "url-hint")}
            style={{ position: "absolute", top: 8, right: 8, background: "none", border: "none", cursor: "pointer", color: "var(--c-text-3)", fontSize: 11 }}
          >
            {copied === "url-hint" ? "✓" : "copy"}
          </button>
        </div>
        <div className="muted text-12 mt-2">Payload: <code style={{ fontSize: 11 }}>{"{ question, responseQuality, suggestedAnswer, assistantId, assistantName, phone }"}</code></div>
      </Card>
    </div>
  );
};

Object.assign(window, { ToolsView, AutomationsView, SystemView, KbGapsView, AsrCorrectionsView });
