/* =========================================================
   Connections — list, status, and per-provider setup flows.
   - OAuth flow:        Google Calendar / Drive, HubSpot, Salesforce, Pipedrive,
                        Zoho CRM, Zoho Mail
   - OAuth + picker:    Google Sheets, Slack, Teams (account → resource)
   - Credentials form:  M-Pesa (Daraja), Airtel Money
   - API key:           Zapier
   - Webhook:           Webhooks (URL + signing secret + retries)
   ========================================================= */

const CAT_LABEL = {
  calendar:   "Calendars",
  payments:   "Payments",
  crm:        "CRM",
  messaging:  "Messaging & email",
  data:       "Data & files",
  automation: "Automation",
  developer:  "Developer",
};
const CAT_ORDER = ["calendar", "payments", "crm", "messaging", "data", "automation", "developer"];

const HEALTH_CHIP = {
  ok:           <Chip kind="live" dot>connected</Chip>,
  warn:         <Chip kind="warn" dot>action needed</Chip>,
  error:        <Chip kind="danger" dot>disconnected</Chip>,
  "needs-setup":<Chip kind="warn">setup required</Chip>,
};

/* ---------------- Page ---------------- */
const TenantConnections = () => {
  const store = MK_USE();
  const toast = useToast();
  const [filter, setFilter] = useState("all"); // all | connected | available | issues
  const [setupId, setSetupId] = useState(null);
  const [manageId, setManageId] = useState(null);
  const [query, setQuery] = useState("");
  const [loading, setLoading] = useState(false);

  const all = store.connections;
  const connected = all.filter(c => c.on);
  const issues    = all.filter(c => c.on && (c.health === "warn" || c.health === "error"));

  const visible = all.filter(c => {
    if (query && !(c.name + " " + c.desc).toLowerCase().includes(query.toLowerCase())) return false;
    if (filter === "connected") return c.on;
    if (filter === "available") return !c.on;
    if (filter === "issues")    return c.on && (c.health === "warn" || c.health === "error");
    return true;
  });

  const grouped = CAT_ORDER.map(cat => [cat, visible.filter(c => c.category === cat)])
                           .filter(([, items]) => items.length);

  const setup = (c) => setSetupId(c.id);
  const manage = (c) => setManageId(c.id);
  const refresh = async (quiet = false) => {
    setLoading(true);
    try {
      await api.connections.list();
      if (!quiet) toast({ title: "Connections refreshed", body: "Live provider status loaded." });
    } catch (err) {
      if (!quiet) toast({ title: "Refresh failed", body: err?.message || "Could not load connection status.", kind: "danger" });
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    refresh(true);
  }, []);

  return (
    <div className="page page-mount">
      <div className="page-header">
        <div>
          <h1 className="page-title">Connections</h1>
          <p className="page-sub">Calendars, payments, CRMs, messaging and webhooks. Mikaka uses these during and after every call.</p>
        </div>
        <div className="row gap-2">
          <button className="btn btn-outline btn-sm" onClick={() => refresh(false)} disabled={loading}>
            <Icon name="refresh" size={14}/> {loading ? "Refreshing..." : "Refresh"}
          </button>
          <a className="btn btn-outline btn-sm" href="https://docs.mikaka.io/connections" target="_blank" rel="noreferrer">
            <Icon name="book" size={14}/> Docs
          </a>
        </div>
      </div>

      {/* Status strip */}
      <div className="grid grid-4" style={{ marginBottom: 24 }}>
        <StatusPill k="Connected" v={connected.length} sub={`of ${all.length} total`} tone="ok"/>
        <StatusPill k="Need attention" v={issues.length} sub="auth or health issues" tone={issues.length ? "warn" : "muted"}
                    onClick={issues.length ? () => setFilter("issues") : null}/>
        <StatusPill k="Sync events · 7d" v={connected.reduce((s, c) => s + (c.syncCount7d || 0), 0).toLocaleString()} sub="across all connections" tone="muted"/>
        <StatusPill k="Webhook" v={all.find(c=>c.id==="webhook")?.on ? "live" : "off"} sub="post call events to your backend" tone={all.find(c=>c.id==="webhook")?.on ? "ok" : "muted"}/>
      </div>

      {/* Filter bar */}
      <div className="row between" style={{ marginBottom: 16, flexWrap: "wrap", gap: 12 }}>
        <div className="seg">
          {[["all", `All · ${all.length}`], ["connected", `Connected · ${connected.length}`], ["available", `Available · ${all.length - connected.length}`], ["issues", `Issues · ${issues.length}`]].map(([k, l]) => (
            <button key={k} className={filter === k ? "on" : ""} onClick={() => setFilter(k)}>{l}</button>
          ))}
        </div>
        <div style={{ position: "relative", maxWidth: 280, flex: 1 }}>
          <Icon name="search" size={14} style={{ position: "absolute", left: 12, top: "50%", transform: "translateY(-50%)", color: "var(--fg-faint)" }}/>
          <input className="input" placeholder="Search connections" value={query} onChange={e => setQuery(e.target.value)} style={{ paddingLeft: 34 }}/>
        </div>
      </div>

      {/* Grouped list */}
      {grouped.length === 0 && (
        <div className="empty">
          <div className="illus"><Icon name="link"/></div>
          <div style={{ fontWeight: 500 }}>No connections match.</div>
          <div style={{ fontSize: 13, color: "var(--fg-muted)", marginTop: 4 }}>Try a different filter or clear search.</div>
        </div>
      )}
      {grouped.map(([cat, items]) => (
        <section key={cat} style={{ marginBottom: 28 }}>
          <div className="uppercase-kick" style={{ marginBottom: 10 }}>{CAT_LABEL[cat]}</div>
          <div className="grid grid-2 stagger">
            {items.map(c => (
              <ConnectionCard key={c.id} c={c} onSetup={() => setup(c)} onManage={() => manage(c)}/>
            ))}
          </div>
        </section>
      ))}

      {/* Modals */}
      {setupId && <SetupRouter id={setupId} onClose={() => setSetupId(null)}/>}
      {manageId && <ManageDrawer id={manageId} onClose={() => setManageId(null)} onReconfigure={() => { setManageId(null); setSetupId(manageId); }}/>}
    </div>
  );
};

/* ---------------- Status pill ---------------- */
const StatusPill = ({ k, v, sub, tone, onClick }) => {
  const dot = { ok: "var(--mk-success)", warn: "var(--mk-warning)", muted: "var(--fg-faint)" }[tone] || "var(--fg-faint)";
  return (
    <div className="card card-pad" onClick={onClick} style={{ cursor: onClick ? "pointer" : "default" }}>
      <div className="row gap-2" style={{ fontSize: 12, color: "var(--fg-muted)", fontWeight: 500 }}>
        <span style={{ width: 8, height: 8, borderRadius: 999, background: dot, display: "inline-block" }}/>
        {k}
      </div>
      <div className="num" style={{ fontSize: 28, marginTop: 8, fontWeight: 600 }}>{v}</div>
      <div style={{ fontSize: 12, color: "var(--fg-muted)", marginTop: 2 }}>{sub}</div>
    </div>
  );
};

/* ---------------- Connection card ---------------- */
const ConnectionCard = ({ c, onSetup, onManage }) => {
  const onClick = c.on ? onManage : onSetup;
  return (
    <div className="card card-pad" style={{ cursor: "pointer" }} onClick={onClick}>
      <div className="row gap-3" style={{ marginBottom: 10 }}>
        <div style={{ width: 44, height: 44, borderRadius: 10, background: "var(--bg-sunken)", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid var(--border)" }}>
          <BrandLogo id={c.logo} fallback={c.icon}/>
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="row gap-2" style={{ flexWrap: "wrap" }}>
            <div style={{ fontWeight: 500, fontSize: 14.5 }}>{c.name}</div>
            <AuthBadge auth={c.auth}/>
          </div>
          {c.acct && <div className="mono" style={{ fontSize: 12, color: "var(--fg-muted)", marginTop: 2 }}>{c.acct}</div>}
        </div>
        {c.on ? HEALTH_CHIP[c.health || "ok"] : <Chip kind="neutral">available</Chip>}
      </div>
      <div style={{ fontSize: 13.5, color: "var(--fg-muted)", marginBottom: 14 }}>{c.desc}</div>
      <div className="row between" style={{ paddingTop: 12, borderTop: "1px solid var(--border)" }}>
        <div style={{ fontSize: 12, color: "var(--fg-faint)" }}>
          {c.on
            ? <>Last sync <b style={{ color: "var(--fg-muted)", fontWeight: 500 }}>{c.lastSync || "—"}</b>{c.syncCount7d != null && <> · {c.syncCount7d} events 7d</>}</>
            : <>Set up in {setupTime(c.auth)}</>}
        </div>
        {c.on
          ? <button className="btn btn-ghost btn-sm" onClick={(e) => { e.stopPropagation(); onManage(); }}>Manage <Icon name="chevron-right" size={12}/></button>
          : <button className="btn btn-primary btn-sm" onClick={(e) => { e.stopPropagation(); onSetup(); }}><Icon name="link" size={12}/> Connect</button>}
      </div>
    </div>
  );
};

const AuthBadge = ({ auth }) => {
  const map = {
    "oauth":       { l: "OAuth",       t: "neutral" },
    "oauth+pick":  { l: "OAuth + setup", t: "neutral" },
    "credentials": { l: "API credentials", t: "neutral" },
    "apikey":      { l: "API key",     t: "neutral" },
    "webhook":     { l: "Webhook",     t: "neutral" },
  };
  const it = map[auth] || { l: auth, t: "neutral" };
  return <span className="chip chip-neutral" style={{ fontSize: 10.5, padding: "2px 7px", textTransform: "uppercase", letterSpacing: ".08em" }}>{it.l}</span>;
};

const setupTime = (auth) => ({
  "oauth": "≈30 sec",
  "oauth+pick": "≈1 min",
  "credentials": "5–10 min",
  "apikey": "≈1 min",
  "webhook": "≈2 min",
}[auth] || "a minute");

/* ====================================================================
   Setup router — picks the right flow per provider auth type.
   ==================================================================== */
const SetupRouter = ({ id, onClose }) => {
  const c = api.getStore().connections.find(x => x.id === id);
  if (!c) return null;
  if (c.id === "mpesa")    return <MpesaSetup c={c} onClose={onClose}/>;
  if (c.id === "airtel")   return <AirtelSetup c={c} onClose={onClose}/>;
  if (c.id === "zapier")   return <ZapierSetup c={c} onClose={onClose}/>;
  if (c.id === "webhook")  return <WebhookSetup c={c} onClose={onClose}/>;
  if (c.id === "gsheets")  return <GoogleSheetsSetup c={c} onClose={onClose}/>;
  if (c.id === "slack" || c.id === "teams") return <ChannelOAuthSetup c={c} onClose={onClose}/>;
  // Default = pure OAuth providers
  return <OAuthSetup c={c} onClose={onClose}/>;
};

/* ---------------- Reusable: stepper ---------------- */
const SetupStepper = ({ steps, current }) => (
  <div className="row gap-2" style={{ marginBottom: 20, flexWrap: "wrap" }}>
    {steps.map((s, i) => {
      const state = i < current ? "done" : i === current ? "on" : "todo";
      return (
        <React.Fragment key={i}>
          <div className="row gap-2" style={{ color: state==="todo" ? "var(--fg-faint)" : state==="on" ? "var(--fg)" : "var(--fg-muted)", fontSize: 12.5, fontWeight: state==="on" ? 500 : 400 }}>
            <span style={{ width: 20, height: 20, borderRadius: 999, display: "inline-flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 600,
              background: state==="done" ? "var(--mk-orange)" : state==="on" ? "var(--mk-ink)" : "var(--bg-sunken)",
              color: state==="todo" ? "var(--fg-faint)" : "#fff",
              border: state==="todo" ? "1px solid var(--border)" : "1px solid transparent" }}>
              {state === "done" ? <Icon name="check" size={11}/> : i + 1}
            </span>
            {s}
          </div>
          {i < steps.length - 1 && <div style={{ flex: "0 0 16px", height: 1, background: "var(--border)" }}/>}
        </React.Fragment>
      );
    })}
  </div>
);

const ProviderHead = ({ c, sub }) => (
  <div className="row gap-3" style={{ marginBottom: 16 }}>
    <div style={{ width: 44, height: 44, borderRadius: 10, background: "var(--bg-sunken)", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid var(--border)" }}>
      <BrandLogo id={c.logo} fallback={c.icon}/>
    </div>
    <div>
      <div style={{ fontWeight: 500, fontSize: 15 }}>Connect {c.name}</div>
      <div style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>{sub}</div>
    </div>
  </div>
);

/* ====================================================================
   FLOW 1 — OAuth (Calendar, Drive, HubSpot, Salesforce, Pipedrive, Zoho)
   Steps: review scopes → "popup" → pick account → confirm
   ==================================================================== */
const SCOPES = {
  gcal:        [["calendar.events", "Read & write events"], ["calendar.readonly", "Read calendar list to choose where bookings go"]],
  gdrive:      [["drive.file", "Read PDFs and docs you explicitly share with Mikaka"], ["drive.metadata.readonly", "List file names so you can pick which ones to ingest"]],
  hubspot:     [["crm.objects.contacts.write", "Create / update contacts"], ["crm.objects.deals.read", "Match calls to existing deals"], ["communication_preferences.read_write", "Honour opt-outs"]],
  salesforce:  [["api", "REST API access"], ["refresh_token", "Stay signed in across token expiry"]],
  pipedrive:   [["contacts:full", "Create contacts and persons"], ["deals:full", "Log call activities on deals"]],
  "zoho-crm":  [["ZohoCRM.modules.ALL", "Create leads, contacts, calls"], ["ZohoCRM.users.READ", "Match call notes to the right owner"]],
  "zoho-mail": [["ZohoMail.messages.CREATE", "Send follow-ups from your inbox"], ["ZohoMail.accounts.READ", "List inboxes so you can pick the sender"]],
};

const OAuthSetup = ({ c, onClose }) => {
  const toast = useToast();
  const [step, setStep] = useState(0); // 0 review · 1 popup · 2 pick · 3 done
  const [accounts, setAccounts] = useState([]);
  const [picked, setPicked] = useState(null);
  const [busy, setBusy] = useState(false);
  const scopes = SCOPES[c.id] || [["read", "Read your data"], ["write", "Write data on your behalf"]];

  const begin = async () => {
    setBusy(true); setStep(1);
    try {
      const r = await api.connections.oauthBegin(c.id);
      const nextAccounts = r.accounts || [];
      setAccounts(nextAccounts);
      setPicked(nextAccounts[0]);
      setStep(2);
    } catch (err) {
      toast({ title: "Authorization required", body: err?.message || "Finish provider authorization, then refresh connections." });
      setStep(0);
    } finally {
      setBusy(false);
    }
  };

  const finish = async () => {
    setBusy(true);
    try {
      await api.connections.connect(c.id, { acct: picked, scopes: scopes.map(s => s[0]) });
      setStep(3);
      toast({ title: `Connected to ${c.name}`, body: picked });
      setTimeout(onClose, 700);
    } catch (err) {
      toast({ title: `Could not connect ${c.name}`, body: err?.message || "Please finish authorization and retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open onClose={onClose} title={`Connect ${c.name}`} size="md"
      footer={
        step === 0 ? <>
          <button className="btn btn-outline" onClick={onClose}>Cancel</button>
          <button className="btn btn-primary" onClick={begin} disabled={busy}><Icon name="link" size={14}/> Continue with {c.name.split(" ")[0]}</button>
        </> :
        step === 2 ? <>
          <button className="btn btn-outline" onClick={() => setStep(0)}>Back</button>
          <button className="btn btn-primary" onClick={finish} disabled={!picked || busy}>{busy ? "Connecting…" : "Connect"}</button>
        </> :
        step === 3 ? <button className="btn btn-primary" onClick={onClose}>Done</button> : null
      }>
      <ProviderHead c={c} sub={`Mikaka will redirect you to ${c.name} to sign in.`}/>
      <SetupStepper steps={["Review", "Sign in", "Pick account", "Done"]} current={step}/>

      {step === 0 && (
        <>
          <div style={{ fontSize: 13, color: "var(--fg-muted)", marginBottom: 12 }}>
            Mikaka will request these permissions. You can revoke them at any time from {c.name} security settings or this page.
          </div>
          <div className="card" style={{ background: "var(--bg-sunken)", padding: 4 }}>
            {scopes.map(([k, d], i) => (
              <div key={i} className="row gap-3" style={{ padding: "10px 12px", borderBottom: i < scopes.length - 1 ? "1px solid var(--border)" : "none" }}>
                <span style={{ width: 22, height: 22, borderRadius: 6, background: "var(--mk-orange-50)", color: "var(--mk-orange-600)", display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
                  <Icon name="check" size={12}/>
                </span>
                <div>
                  <div className="mono" style={{ fontSize: 12 }}>{k}</div>
                  <div style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>{d}</div>
                </div>
              </div>
            ))}
          </div>
          <div style={{ fontSize: 11.5, color: "var(--fg-faint)", marginTop: 12 }}>
            Mikaka never stores your password. Tokens are encrypted at rest with envelope encryption (AWS KMS).
          </div>
        </>
      )}

      {step === 1 && (
        <div className="empty" style={{ padding: 32 }}>
          <div className="illus"><Icon name="link"/></div>
          <div style={{ fontWeight: 500 }}>Waiting for {c.name}…</div>
          <div style={{ fontSize: 12.5, color: "var(--fg-muted)", marginTop: 6 }}>A popup window opened — sign in there. We'll continue automatically.</div>
        </div>
      )}

      {step === 2 && (
        <>
          <div style={{ fontSize: 13, color: "var(--fg-muted)", marginBottom: 12 }}>Pick the account Mikaka should use. You can connect more than one later.</div>
          <div className="stack gap-2">
            {accounts.map(a => (
              <label key={a} className="card card-pad row gap-3" style={{ padding: 12, cursor: "pointer", borderColor: picked === a ? "var(--mk-orange)" : "var(--border)", background: picked === a ? "var(--mk-orange-50)" : "var(--bg-elev)" }}>
                <input type="radio" checked={picked === a} onChange={() => setPicked(a)} style={{ accentColor: "var(--mk-orange)" }}/>
                <div style={{ width: 32, height: 32, borderRadius: 999, background: "var(--bg-sunken)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 12, fontWeight: 600 }}>{a.slice(0,1).toUpperCase()}</div>
                <div className="mono" style={{ fontSize: 13 }}>{a}</div>
              </label>
            ))}
          </div>
        </>
      )}

      {step === 3 && (
        <div className="empty" style={{ padding: 32, borderColor: "var(--mk-success)" }}>
          <div className="illus" style={{ background: "rgba(27,158,107,.1)", color: "var(--mk-success)" }}><Icon name="check"/></div>
          <div style={{ fontWeight: 500 }}>Connected.</div>
          <div style={{ fontSize: 12.5, color: "var(--fg-muted)", marginTop: 4 }}>{picked}</div>
        </div>
      )}
    </Modal>
  );
};

/* ====================================================================
   FLOW 2 — Channel-style OAuth: Slack / Teams.
   Adds a workspace + channel picker after sign-in.
   ==================================================================== */
const ChannelOAuthSetup = ({ c, onClose }) => {
  const toast = useToast();
  const [step, setStep] = useState(0);
  const [accounts, setAccounts] = useState([]);
  const [picked, setPicked] = useState(null);
  const [channels, setChannels] = useState([]);
  const [channel, setChannel] = useState(null);
  const [events, setEvents] = useState({ missed: true, escalation: true, booking: false, summary: false });
  const [busy, setBusy] = useState(false);

  const begin = async () => {
    setBusy(true); setStep(1);
    try {
      const r = await api.connections.oauthBegin(c.id);
      const nextAccounts = r.accounts || [];
      setAccounts(nextAccounts); setPicked(nextAccounts[0]);
      const ch = await api.connections.listChannels(c.id);
      setChannels(ch); setChannel(ch[0]);
      setStep(2);
    } catch (err) {
      toast({ title: `Could not start ${c.name}`, body: err?.message || "Open the provider authorization page and retry.", kind: "danger" });
      setStep(0);
    } finally {
      setBusy(false);
    }
  };
  const finish = async () => {
    setBusy(true);
    try {
      await api.connections.connect(c.id, { acct: `${picked} · ${channel}`, config: { channel, events } });
      setStep(3);
      toast({ title: `${c.name} connected`, body: `Posting to ${channel}` });
      setTimeout(onClose, 700);
    } catch (err) {
      toast({ title: `Could not connect ${c.name}`, body: err?.message || "Please retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open onClose={onClose} title={`Connect ${c.name}`} size="md"
      footer={
        step === 0 ? <>
          <button className="btn btn-outline" onClick={onClose}>Cancel</button>
          <button className="btn btn-primary" onClick={begin}><Icon name="link" size={14}/> Sign in to {c.name}</button>
        </> :
        step === 2 ? <>
          <button className="btn btn-outline" onClick={() => setStep(0)}>Back</button>
          <button className="btn btn-primary" onClick={finish} disabled={!channel || busy}>{busy ? "Connecting…" : "Save & connect"}</button>
        </> :
        step === 3 ? <button className="btn btn-primary" onClick={onClose}>Done</button> : null
      }>
      <ProviderHead c={c} sub="Pick a workspace, the channel to post to, and which events to send."/>
      <SetupStepper steps={["Review", "Sign in", "Pick channel", "Done"]} current={step}/>

      {step === 0 && (
        <div className="card card-pad" style={{ background: "var(--bg-sunken)" }}>
          <div style={{ fontSize: 13, color: "var(--fg-muted)" }}>
            Mikaka will install a bot user in your {c.name} workspace and request permission to post messages to the channel you choose.
            Required scopes: <span className="mono">chat:write</span>, <span className="mono">channels:read</span>, <span className="mono">groups:read</span>.
          </div>
        </div>
      )}

      {step === 1 && (
        <div className="empty"><div className="illus"><Icon name="link"/></div><div>Waiting for {c.name}…</div></div>
      )}

      {step === 2 && (
        <div className="stack gap-4">
          <div className="field">
            <label>Workspace</label>
            <select className="input" value={picked || ""} onChange={e => setPicked(e.target.value)}>
              {accounts.map(a => <option key={a}>{a}</option>)}
            </select>
          </div>
          <div className="field">
            <label>Channel</label>
            <select className="input" value={channel || ""} onChange={e => setChannel(e.target.value)}>
              {channels.map(ch => <option key={ch}>{ch}</option>)}
            </select>
            <div className="hint">Don't see the channel? Invite the Mikaka bot first, then refresh.</div>
          </div>
          <div>
            <label style={{ fontSize: 13, fontWeight: 500, display: "block", marginBottom: 8 }}>Send to {channel || "this channel"} when…</label>
            <div className="stack gap-2">
              {[
                ["missed", "A call is missed and a callback is needed"],
                ["escalation", "Mikaka escalates a call to a human"],
                ["booking", "A new booking is made"],
                ["summary", "Daily 7 am call summary"],
              ].map(([k, l]) => (
                <label key={k} className="row gap-3" style={{ cursor: "pointer", padding: "8px 10px", borderRadius: 8, background: events[k] ? "var(--mk-orange-50)" : "var(--bg-sunken)" }}>
                  <div className={`switch ${events[k] ? "on" : ""}`} onClick={() => setEvents(s => ({ ...s, [k]: !s[k] }))}/>
                  <span style={{ fontSize: 13 }}>{l}</span>
                </label>
              ))}
            </div>
          </div>
        </div>
      )}

      {step === 3 && (
        <div className="empty" style={{ padding: 32, borderColor: "var(--mk-success)" }}>
          <div className="illus" style={{ background: "rgba(27,158,107,.1)", color: "var(--mk-success)" }}><Icon name="check"/></div>
          <div style={{ fontWeight: 500 }}>{c.name} is live in {channel}.</div>
        </div>
      )}
    </Modal>
  );
};

/* ====================================================================
   FLOW 3 — Google Sheets (OAuth + spreadsheet picker + column mapping)
   ==================================================================== */
const SheetEvent = "call.ended";
const DEFAULT_COLUMNS = [
  ["Timestamp",   "{{call.started_at}}"],
  ["From",        "{{call.from}}"],
  ["Intent",      "{{call.intent}}"],
  ["Outcome",     "{{call.outcome}}"],
  ["Duration",    "{{call.duration_s}}"],
  ["Summary",     "{{call.summary}}"],
];

const GoogleSheetsSetup = ({ c, onClose }) => {
  const toast = useToast();
  const [step, setStep] = useState(0); // 0 review · 1 oauth · 2 pick sheet · 3 columns · 4 done
  const [accounts, setAccounts] = useState([]);
  const [picked, setPicked] = useState(null);
  const [sheets, setSheets] = useState([]);
  const [sheet, setSheet] = useState(null);
  const [manualSheetId, setManualSheetId] = useState("");
  const [manualSheetName, setManualSheetName] = useState("Mikaka Calls");
  const [tab, setTab] = useState("Sheet1");
  const [columns, setColumns] = useState(DEFAULT_COLUMNS);
  const [trigger, setTrigger] = useState(SheetEvent);
  const [busy, setBusy] = useState(false);

  const begin = async () => {
    setBusy(true); setStep(1);
    try {
      const r = await api.connections.oauthBegin("gsheets");
      const nextAccounts = r.accounts || [];
      setAccounts(nextAccounts); setPicked(nextAccounts[0]);
    } catch (err) {
      toast({ title: "Google authorization", body: err?.message || "Finish Google authorization, then paste the sheet ID here." });
      setAccounts(["Google authorization pending"]);
      setPicked("Google authorization pending");
    }
    try {
      const ss = await api.connections.listSheets();
      setSheets(ss); setSheet(ss[0]);
    } catch {
      setSheets([]); setSheet(null);
    }
    setBusy(false); setStep(2);
  };

  const finish = async () => {
    setBusy(true);
    const finalSheet = sheet || { id: manualSheetId.trim(), name: manualSheetName.trim() || "Google Sheet" };
    try {
      await api.connections.connect("gsheets", {
        acct: `${picked || "Google"} -> ${finalSheet.name} · ${tab}`,
        config: { trigger, sheetId: finalSheet.id, tab, columns: Object.fromEntries(columns) },
      });
      setStep(4);
      toast({ title: "Google Sheets connected", body: `Appending to ${finalSheet.name} -> ${tab}` });
      setTimeout(onClose, 800);
    } catch (err) {
      toast({ title: "Could not save Google Sheets", body: err?.message || "Please retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };

  const updateCol = (i, idx, val) => {
    setColumns(cols => cols.map((c, j) => j === i ? (idx === 0 ? [val, c[1]] : [c[0], val]) : c));
  };

  return (
    <Modal open onClose={onClose} title="Connect Google Sheets" size="lg"
      footer={
        step === 0 ? <><button className="btn btn-outline" onClick={onClose}>Cancel</button><button className="btn btn-primary" onClick={begin}><Icon name="link" size={14}/> Continue with Google</button></> :
        step === 2 ? <><button className="btn btn-outline" onClick={() => setStep(0)}>Back</button><button className="btn btn-primary" onClick={() => setStep(3)} disabled={!sheet && !manualSheetId.trim()}>Next: map columns -></button></> :
        step === 3 ? <><button className="btn btn-outline" onClick={() => setStep(2)}>Back</button><button className="btn btn-primary" onClick={finish} disabled={busy}>{busy ? "Saving…" : "Save & connect"}</button></> :
        step === 4 ? <button className="btn btn-primary" onClick={onClose}>Done</button> : null
      }>
      <ProviderHead c={c} sub="Append a row to a Google Sheet for every call."/>
      <SetupStepper steps={["Review", "Sign in", "Pick sheet", "Map columns", "Done"]} current={step}/>

      {step === 0 && (
        <>
          <div className="card card-pad" style={{ background: "var(--bg-sunken)", marginBottom: 12 }}>
            <div style={{ fontSize: 13, color: "var(--fg-muted)" }}>
              We'll request <span className="mono">drive.file</span> (so we only see the spreadsheet you pick) and <span className="mono">spreadsheets</span> to append rows.
              Mikaka never reads the rest of your Drive.
            </div>
          </div>
          <div style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>
            <b style={{ color: "var(--fg)" }}>Recommended:</b> create a fresh sheet just for Mikaka. The first row will become headers; subsequent rows are call records.
          </div>
        </>
      )}

      {step === 1 && <div className="empty"><div className="illus"><Icon name="link"/></div><div>Waiting for Google…</div></div>}

      {step === 2 && (
        <div className="stack gap-4">
          <div className="field">
            <label>Google account</label>
            <select className="input" value={picked || ""} onChange={e => setPicked(e.target.value)}>
              {accounts.map(a => <option key={a}>{a}</option>)}
            </select>
          </div>
          <div className="field">
            <label>Spreadsheet</label>
            <div className="card" style={{ padding: 4 }}>
              {sheets.length > 0 ? sheets.map(s => (
                <label key={s.id} className="row gap-3" style={{ padding: "10px 12px", cursor: "pointer", borderRadius: 8, background: sheet?.id === s.id ? "var(--mk-orange-50)" : "transparent" }}>
                  <input type="radio" checked={sheet?.id === s.id} onChange={() => setSheet(s)} style={{ accentColor: "var(--mk-orange)" }}/>
                  <BrandLogo id="google-sheets" fallback="list"/>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 13.5, fontWeight: 500 }}>{s.name}</div>
                    <div className="mono" style={{ fontSize: 11.5, color: "var(--fg-faint)" }}>{s.id} · updated {s.updated}{s.owner ? ` · ${s.owner}` : ""}</div>
                  </div>
                </label>
              )) : (
                <div className="stack gap-3" style={{ padding: 12 }}>
                  <div style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>Paste the spreadsheet ID or URL after authorizing Google.</div>
                  <input className="input mono" placeholder="Spreadsheet ID or URL" value={manualSheetId} onChange={(e) => {
                    const value = e.target.value.trim();
                    const match = value.match(/\/spreadsheets\/d\/([^/]+)/);
                    setManualSheetId(match?.[1] || value);
                    setSheet(null);
                  }}/>
                  <input className="input" placeholder="Display name" value={manualSheetName} onChange={(e) => setManualSheetName(e.target.value)}/>
                </div>
              )}
            </div>
            <div className="hint row gap-2" style={{ marginTop: 8 }}>
              <Icon name="plus" size={12}/> <a className="dot-link">Create a new sheet</a> · or <a className="dot-link">paste a sheet URL</a>
            </div>
          </div>
          <div className="field" style={{ maxWidth: 240 }}>
            <label>Tab</label>
            <input className="input" value={tab} onChange={e => setTab(e.target.value)}/>
          </div>
        </div>
      )}

      {step === 3 && (
        <div className="stack gap-4">
          <div className="field" style={{ maxWidth: 360 }}>
            <label>Append a row when…</label>
            <select className="input" value={trigger} onChange={e => setTrigger(e.target.value)}>
              <option value="call.ended">A call ends (recommended)</option>
              <option value="call.booked">A booking is created</option>
              <option value="call.escalated">A call is escalated to a human</option>
              <option value="call.payment">A payment is collected</option>
            </select>
          </div>
          <div>
            <label style={{ fontSize: 13, fontWeight: 500, display: "block", marginBottom: 8 }}>Columns</label>
            <div className="card" style={{ background: "var(--bg-sunken)", padding: 4 }}>
              <div className="row" style={{ padding: "8px 12px", fontSize: 11, color: "var(--fg-muted)", textTransform: "uppercase", letterSpacing: ".08em", fontWeight: 600 }}>
                <div style={{ flex: 1 }}>Header</div>
                <div style={{ flex: 1.5 }}>Mapped value</div>
                <div style={{ width: 32 }}/>
              </div>
              {columns.map(([h, v], i) => (
                <div key={i} className="row gap-2" style={{ padding: "6px 8px" }}>
                  <input className="input" value={h} onChange={e => updateCol(i, 0, e.target.value)} style={{ flex: 1 }}/>
                  <input className="input mono" value={v} onChange={e => updateCol(i, 1, e.target.value)} style={{ flex: 1.5, fontSize: 12 }}/>
                  <button className="btn btn-ghost btn-icon btn-sm" onClick={() => setColumns(cs => cs.filter((_, j) => j !== i))}><Icon name="x" size={12}/></button>
                </div>
              ))}
              <button className="btn btn-ghost btn-sm" style={{ margin: 6 }} onClick={() => setColumns(cs => [...cs, ["New column", "{{call.field}}"]])}>
                <Icon name="plus" size={12}/> Add column
              </button>
            </div>
            <div className="hint" style={{ marginTop: 6 }}>Use <span className="mono">{`{{call.field}}`}</span> tokens. See <a className="dot-link">all tokens</a>.</div>
          </div>
        </div>
      )}

      {step === 4 && (
        <div className="empty" style={{ borderColor: "var(--mk-success)" }}>
          <div className="illus" style={{ background: "rgba(27,158,107,.1)", color: "var(--mk-success)" }}><Icon name="check"/></div>
          <div style={{ fontWeight: 500 }}>Sheet is now logging calls.</div>
          <div style={{ fontSize: 12.5, color: "var(--fg-muted)", marginTop: 4 }}>Open <a className="dot-link">{sheet?.name || manualSheetName}</a> to see new rows.</div>
        </div>
      )}
    </Modal>
  );
};

/* ====================================================================
   FLOW 4 — M-Pesa (Daraja credentials)
   ==================================================================== */
const MpesaSetup = ({ c, onClose }) => {
  const toast = useToast();
  const [step, setStep] = useState(0); // 0 prereq · 1 type · 2 creds · 3 callbacks · 4 test · 5 done
  const [type, setType] = useState("till");          // till | paybill
  const [env, setEnv] = useState("sandbox");
  const [shortcode, setShortcode] = useState("");
  const [paybill, setPaybill] = useState("");
  const [accountRef, setAccountRef] = useState("");
  const [consumerKey, setConsumerKey] = useState("");
  const [consumerSecret, setConsumerSecret] = useState("");
  const [passkey, setPasskey] = useState("");
  const [maxAmount, setMaxAmount] = useState(10000);
  const [test, setTest] = useState(null);
  const [busy, setBusy] = useState(false);

  const callbackUrl = `https://api.mikaka.io/v1/webhooks/mpesa/${env}/acme-${type}-${shortcode || "____"}`;
  const validationUrl = callbackUrl.replace("/mpesa/", "/mpesa-validation/");

  const runTest = async () => {
    setBusy(true);
    try {
      const r = await api.connections.testMpesa();
      setTest(r);
    } catch (err) {
      setTest({ ok: false, msg: err?.message || "Could not verify platform rail" });
    } finally {
      setBusy(false);
    }
  };

  const finish = async () => {
    setBusy(true);
    try {
      await api.connections.connect("mpesa", {
        acct: `${type === "till" ? "Till" : "Paybill"} ${shortcode || paybill} · ${env === "sandbox" ? "Sandbox" : "Production"}`,
        health: "ok",
        config: { type, env, shortcode, paybill, accountRef, maxAmount, callbackUrl, validationUrl },
      });
      setStep(5);
      toast({ title: "M-Pesa connected", body: "STK push enabled for inbound calls." });
      setTimeout(onClose, 800);
    } catch (err) {
      toast({ title: "Could not connect M-Pesa", body: err?.message || "Please retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };

  const credsOk = consumerKey.length > 4 && consumerSecret.length > 4 && (type === "till" ? shortcode : paybill);

  return (
    <Modal open onClose={onClose} title="Connect M-Pesa (Daraja)" size="lg"
      footer={
        step === 0 ? <><button className="btn btn-outline" onClick={onClose}>Cancel</button><button className="btn btn-primary" onClick={() => setStep(1)}>I have a Daraja app →</button></> :
        step === 1 ? <><button className="btn btn-outline" onClick={() => setStep(0)}>Back</button><button className="btn btn-primary" onClick={() => setStep(2)}>Next →</button></> :
        step === 2 ? <><button className="btn btn-outline" onClick={() => setStep(1)}>Back</button><button className="btn btn-primary" onClick={() => setStep(3)} disabled={!credsOk}>Next: callbacks →</button></> :
        step === 3 ? <><button className="btn btn-outline" onClick={() => setStep(2)}>Back</button><button className="btn btn-primary" onClick={() => setStep(4)}>Next: test →</button></> :
        step === 4 ? <><button className="btn btn-outline" onClick={() => setStep(3)}>Back</button><button className="btn btn-primary" onClick={finish} disabled={!credsOk || busy}>{busy ? "Saving..." : "Save & connect"}</button></> :
        step === 5 ? <button className="btn btn-primary" onClick={onClose}>Done</button> : null
      }>
      <ProviderHead c={c} sub="Use Safaricom Daraja credentials so Mikaka can trigger STK push and receive C2B notifications during calls."/>
      <SetupStepper steps={["Prerequisites", "Account type", "Credentials", "Callbacks", "Test", "Done"]} current={step}/>

      {step === 0 && (
        <>
          <div className="card card-pad" style={{ background: "var(--bg-sunken)", marginBottom: 12 }}>
            <div style={{ fontWeight: 500, marginBottom: 8 }}>Before you start</div>
            <ol style={{ fontSize: 13, color: "var(--fg-muted)", paddingLeft: 18, margin: 0, lineHeight: 1.7 }}>
              <li>Sign in to <a className="dot-link" target="_blank" rel="noreferrer" href="https://developer.safaricom.co.ke">developer.safaricom.co.ke</a> with the same number that owns your Till / Paybill.</li>
              <li>Create a new app, enable <b>M-Pesa Express (STK push)</b> and <b>Customer to Business</b>.</li>
              <li>Copy the <b>Consumer Key</b> and <b>Consumer Secret</b>. For STK push you'll also need the <b>Passkey</b> (Daraja → My Apps → Lipa Na M-Pesa Online).</li>
              <li>For production, you'll need to "Go Live" in the Daraja portal — sandbox lets you test end-to-end first.</li>
            </ol>
          </div>
          <div className="row gap-2" style={{ fontSize: 12.5 }}>
            <Icon name="shield" size={14} style={{ color: "var(--fg-muted)" }}/>
            <span style={{ color: "var(--fg-muted)" }}>Mikaka stores your secret encrypted with AWS KMS. We never log full secrets, only the last 4 chars.</span>
          </div>
        </>
      )}

      {step === 1 && (
        <>
          <div style={{ fontSize: 13, color: "var(--fg-muted)", marginBottom: 12 }}>What will Mikaka collect on?</div>
          <div className="grid grid-2 gap-3">
            {[
              ["till",    "Till Number (Buy Goods)", "Customer enters Till number on their phone. Best for retail / single product price."],
              ["paybill", "Paybill (Bill payment)",  "Customer enters Paybill + an account reference. Best for invoices, deposits, deals where each customer has an account."],
            ].map(([k, t, d]) => (
              <button key={k} type="button" className="template-pick" data-on={type === k} style={{ borderColor: type === k ? "var(--mk-orange)" : "var(--border)", background: type === k ? "var(--mk-orange-50)" : "var(--bg)" }} onClick={() => setType(k)}>
                <div style={{ fontWeight: 500, marginBottom: 6 }}>{t}</div>
                <div style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>{d}</div>
              </button>
            ))}
          </div>
          <div className="row gap-2" style={{ marginTop: 16 }}>
            <span style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>Environment:</span>
            <div className="seg">
              <button className={env === "sandbox" ? "on" : ""} onClick={() => setEnv("sandbox")}>Sandbox</button>
              <button className={env === "production" ? "on" : ""} onClick={() => setEnv("production")}>Production</button>
            </div>
            {env === "sandbox" && <span style={{ fontSize: 12, color: "var(--fg-muted)" }}>· Use the Daraja sandbox phone <span className="mono">254708374149</span> to test.</span>}
          </div>
        </>
      )}

      {step === 2 && (
        <div className="stack gap-3">
          <div className="grid grid-2 gap-3">
            {type === "till" ? (
              <div className="field"><label>Till number</label><input className="input mono" placeholder="174522" value={shortcode} onChange={e => setShortcode(e.target.value)}/></div>
            ) : (
              <>
                <div className="field"><label>Paybill (head office)</label><input className="input mono" placeholder="600000" value={paybill} onChange={e => setPaybill(e.target.value)}/></div>
                <div className="field"><label>Default account reference</label><input className="input" placeholder="e.g. {{caller.phone}}" value={accountRef} onChange={e => setAccountRef(e.target.value)}/><div className="hint">Tokens allowed.</div></div>
              </>
            )}
          </div>
          <div className="field"><label>Daraja consumer key</label><input className="input mono" placeholder="abCD123… (≥ 16 chars)" value={consumerKey} onChange={e => setConsumerKey(e.target.value)}/></div>
          <div className="field"><label>Daraja consumer secret</label><input className="input mono" type="password" placeholder="••••••••" value={consumerSecret} onChange={e => setConsumerSecret(e.target.value)}/></div>
          <div className="field"><label>Lipa Na M-Pesa Online passkey <span style={{ color: "var(--fg-faint)", fontWeight: 400 }}>(STK push only)</span></label><input className="input mono" type="password" placeholder="••••••••" value={passkey} onChange={e => setPasskey(e.target.value)}/></div>
          <div className="field"><label>Max amount per call (KSh)</label><input className="input" type="number" value={maxAmount} onChange={e => setMaxAmount(+e.target.value)}/><div className="hint">A guardrail — Mikaka will refuse to charge more than this in any single call.</div></div>
        </div>
      )}

      {step === 3 && (
        <>
          <div style={{ fontSize: 13, color: "var(--fg-muted)", marginBottom: 12 }}>
            Paste these into Daraja → My Apps → <b>{type === "till" ? "Confirmation & Validation URLs (Buy Goods)" : "Register URLs"}</b>. Both must be HTTPS and reachable from Safaricom's network — they already are; these are Mikaka-hosted.
          </div>
          <div className="card" style={{ padding: 0, marginBottom: 12 }}>
            <CodeRow label="Confirmation URL" value={callbackUrl}/>
            <CodeRow label="Validation URL"   value={validationUrl}/>
          </div>
          <div className="card card-pad" style={{ background: "var(--bg-sunken)", fontSize: 12.5, color: "var(--fg-muted)" }}>
            <div className="row gap-2" style={{ color: "var(--mk-warning)", fontWeight: 500, marginBottom: 6 }}>
              <Icon name="alert" size={14}/> Important
            </div>
            On production, Safaricom requires you to whitelist Mikaka's IPs. Send a request via the M-Pesa Business portal: <span className="mono">196.201.214.0/24</span>. We open a support ticket on your behalf if needed.
          </div>
        </>
      )}

      {step === 4 && (
        <>
          <div style={{ fontSize: 13, color: "var(--fg-muted)", marginBottom: 12 }}>We'll verify the Mikaka platform rail before saving your tenant payment setup. No money moves.</div>
          <button className="btn btn-primary" onClick={runTest} disabled={busy}>{busy ? "Testing…" : <><Icon name="play" size={12}/> Run test</>}</button>
          {test && (
            <div className="card card-pad" style={{ marginTop: 14, borderColor: test.ok ? "var(--mk-success)" : "var(--mk-danger)", background: test.ok ? "rgba(27,158,107,.06)" : "rgba(208,69,58,.06)" }}>
              <div className="row gap-2" style={{ fontWeight: 500, color: test.ok ? "var(--mk-success)" : "var(--mk-danger)" }}>
                <Icon name={test.ok ? "check" : "alert"} size={14}/>{test.ok ? "Platform rail OK" : "Platform rail not connected yet"}
              </div>
              <div style={{ fontSize: 12.5, color: "var(--fg-muted)", marginTop: 6 }}>{test.msg}</div>
              {test.ok && <div className="mono" style={{ fontSize: 11.5, color: "var(--fg-faint)", marginTop: 6 }}>access_token={test.token} · expires {test.expires}</div>}
            </div>
          )}
        </>
      )}

      {step === 5 && (
        <div className="empty" style={{ borderColor: "var(--mk-success)" }}>
          <div className="illus" style={{ background: "rgba(27,158,107,.1)", color: "var(--mk-success)" }}><Icon name="check"/></div>
          <div style={{ fontWeight: 500 }}>M-Pesa is live.</div>
          <div style={{ fontSize: 12.5, color: "var(--fg-muted)", marginTop: 4 }}>Mikaka can now trigger STK push during calls (max KSh {maxAmount.toLocaleString()}/call).</div>
        </div>
      )}
    </Modal>
  );
};

const CodeRow = ({ label, value }) => {
  const [copied, setCopied] = useState(false);
  const copy = () => { navigator.clipboard?.writeText(value); setCopied(true); setTimeout(() => setCopied(false), 1200); };
  return (
    <div className="row gap-3" style={{ padding: "12px 14px", borderBottom: "1px solid var(--border)" }}>
      <div style={{ width: 140, fontSize: 12, color: "var(--fg-muted)", fontWeight: 500 }}>{label}</div>
      <div className="mono" style={{ flex: 1, fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: "var(--fg)" }}>{value}</div>
      <button className="btn btn-ghost btn-sm" onClick={copy}>{copied ? <><Icon name="check" size={12}/> Copied</> : <><Icon name="copy" size={12}/> Copy</>}</button>
    </div>
  );
};

/* ====================================================================
   FLOW 5 — Airtel Money (similar to M-Pesa but simpler)
   ==================================================================== */
const AirtelSetup = ({ c, onClose }) => {
  const toast = useToast();
  const [step, setStep] = useState(0);
  const [merchantId, setMerchantId] = useState("");
  const [clientId, setClientId] = useState("");
  const [clientSecret, setClientSecret] = useState("");
  const [country, setCountry] = useState("KE");
  const [busy, setBusy] = useState(false);

  const finish = async () => {
    setBusy(true);
    try {
      await api.connections.connect("airtel", {
        acct: `Merchant ${merchantId} · ${country}`,
        config: { merchantId, clientId, clientSecret, country },
        credentials: { api_key: clientSecret },
      });
      setStep(2);
      toast({ title: "Airtel Money connected" });
      setTimeout(onClose, 700);
    } catch (err) {
      toast({ title: "Could not connect Airtel Money", body: err?.message || "Please retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open onClose={onClose} title="Connect Airtel Money" size="md"
      footer={
        step === 0 ? <><button className="btn btn-outline" onClick={onClose}>Cancel</button><button className="btn btn-primary" onClick={() => setStep(1)}>Continue →</button></> :
        step === 1 ? <><button className="btn btn-outline" onClick={() => setStep(0)}>Back</button><button className="btn btn-primary" disabled={busy || !merchantId || !clientSecret} onClick={finish}>{busy ? "Saving…" : "Save & connect"}</button></> :
        <button className="btn btn-primary" onClick={onClose}>Done</button>
      }>
      <ProviderHead c={c} sub="Get credentials from openapiuat.airtel.africa → My Apps."/>
      <SetupStepper steps={["Prerequisites", "Credentials", "Done"]} current={step}/>

      {step === 0 && (
        <div className="card card-pad" style={{ background: "var(--bg-sunken)", fontSize: 13, color: "var(--fg-muted)" }}>
          You'll need an approved Airtel Money merchant ID and OAuth credentials from <a className="dot-link" target="_blank" rel="noreferrer" href="https://developers.airtel.africa">developers.airtel.africa</a>. Approval typically takes 2–3 business days.
        </div>
      )}
      {step === 1 && (
        <div className="stack gap-3">
          <div className="field"><label>Country</label>
            <select className="input" value={country} onChange={e => setCountry(e.target.value)}>
              <option value="KE">Kenya</option><option value="UG">Uganda</option><option value="TZ">Tanzania</option><option value="MW">Malawi</option><option value="ZM">Zambia</option><option value="NG">Nigeria</option>
            </select>
          </div>
          <div className="field"><label>Merchant ID</label><input className="input mono" value={merchantId} onChange={e => setMerchantId(e.target.value)}/></div>
          <div className="field"><label>Client ID</label><input className="input mono" value={clientId} onChange={e => setClientId(e.target.value)}/></div>
          <div className="field"><label>Client secret</label><input className="input mono" type="password" value={clientSecret} onChange={e => setClientSecret(e.target.value)}/></div>
        </div>
      )}
      {step === 2 && <div className="empty" style={{ borderColor: "var(--mk-success)" }}><div className="illus" style={{ background: "rgba(27,158,107,.1)", color: "var(--mk-success)" }}><Icon name="check"/></div><div style={{ fontWeight: 500 }}>Airtel Money connected.</div></div>}
    </Modal>
  );
};

/* ====================================================================
   FLOW 6 — Zapier (API key + workspace)
   ==================================================================== */
const ZapierSetup = ({ c, onClose }) => {
  const toast = useToast();
  const [key] = useState(() => "mk_live_" + Math.random().toString(36).slice(2, 12) + Math.random().toString(36).slice(2, 8));
  const [revealed, setRevealed] = useState(false);
  const [installed, setInstalled] = useState(false);
  const [busy, setBusy] = useState(false);

  const finish = async () => {
    setBusy(true);
    try {
      await api.connections.connect("zapier", { acct: "Personal workspace", config: { key } });
      toast({ title: "Zapier connected", body: "Mikaka triggers and actions are now available in Zapier." });
      onClose();
    } catch (err) {
      toast({ title: "Could not connect Zapier", body: err?.message || "Please retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open onClose={onClose} title="Connect Zapier" size="md"
      footer={<><button className="btn btn-outline" onClick={onClose}>Cancel</button><button className="btn btn-primary" disabled={!installed || busy} onClick={finish}>{busy ? "Saving…" : "Done — I've installed the Zap"}</button></>}>
      <ProviderHead c={c} sub="Use Mikaka triggers (call.ended, booking.created) inside any Zap."/>
      <div className="stack gap-3">
        <div className="card card-pad" style={{ background: "var(--bg-sunken)" }}>
          <div style={{ fontSize: 13, fontWeight: 500, marginBottom: 6 }}>1. Copy your API key</div>
          <div className="row gap-2">
            <input className="input mono" readOnly value={revealed ? key : "•".repeat(24)} style={{ flex: 1, fontSize: 12 }}/>
            <button className="btn btn-outline btn-sm" onClick={() => setRevealed(r => !r)}>{revealed ? "Hide" : "Reveal"}</button>
            <button className="btn btn-outline btn-sm" onClick={() => navigator.clipboard?.writeText(key)}><Icon name="copy" size={12}/> Copy</button>
          </div>
          <div className="hint" style={{ marginTop: 6 }}>This key is shown once — store it in a password manager.</div>
        </div>
        <div className="card card-pad" style={{ background: "var(--bg-sunken)" }}>
          <div style={{ fontSize: 13, fontWeight: 500, marginBottom: 6 }}>2. Install the Mikaka Zapier app</div>
          <div style={{ fontSize: 12.5, color: "var(--fg-muted)", marginBottom: 8 }}>Open the public app page and authorize. When prompted, paste the API key above.</div>
          <a className="btn btn-outline btn-sm" target="_blank" rel="noreferrer" href="https://zapier.com/apps/mikaka" onClick={() => setInstalled(true)}><Icon name="external-link" size={12}/> Open Zapier</a>
        </div>
        <div className="card card-pad" style={{ background: "var(--bg-sunken)" }}>
          <div style={{ fontSize: 13, fontWeight: 500, marginBottom: 6 }}>3. Pick a starter Zap (optional)</div>
          <div className="stack gap-2">
            {["Add new caller to Mailchimp audience", "Create a Notion page for every booking", "Send Twilio SMS on missed call"].map(z => (
              <div key={z} className="row between" style={{ padding: "6px 0" }}>
                <span style={{ fontSize: 12.5 }}>{z}</span>
                <a className="dot-link" style={{ fontSize: 12 }}>Use template</a>
              </div>
            ))}
          </div>
        </div>
      </div>
    </Modal>
  );
};

/* ====================================================================
   FLOW 7 — Webhooks (URL + signing secret + events + retry policy + test)
   ==================================================================== */
const WebhookSetup = ({ c, onClose }) => {
  const toast = useToast();
  const [url, setUrl] = useState("");
  const [secret] = useState(() => "whsec_" + Math.random().toString(36).slice(2, 26));
  const [events, setEvents] = useState({
    "call.started": false, "call.ended": true, "call.transcript": false, "booking.created": true, "payment.collected": true, "callback.requested": true,
  });
  const [version, setVersion] = useState("2026-01");
  const [retries, setRetries] = useState(5);
  const [test, setTest] = useState(null);
  const [busy, setBusy] = useState(false);

  const runTest = async () => {
    setBusy(true);
    try {
      const r = await api.connections.testWebhook(url, { secret, events, version, retries });
      setTest(r);
    } catch (err) {
      setTest({ ok: false, code: "error", ms: 0, body: err?.message || "Webhook test failed" });
    } finally {
      setBusy(false);
    }
  };
  const finish = async () => {
    setBusy(true);
    try {
      await api.connections.connect("webhook", { acct: url, config: { url, secret, events, version, retries } });
      toast({ title: "Webhook live", body: url });
      onClose();
    } catch (err) {
      toast({ title: "Could not save webhook", body: err?.message || "Please retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };
  const validUrl = /^https:\/\//i.test(url);

  return (
    <Modal open onClose={onClose} title="Configure webhook" size="lg"
      footer={<><button className="btn btn-outline" onClick={onClose}>Cancel</button><button className="btn btn-primary" disabled={!validUrl || busy} onClick={finish}>{busy ? "Saving..." : "Save webhook"}</button></>}>
      <ProviderHead c={c} sub="Mikaka POSTs JSON events to your URL with an HMAC-SHA256 signature."/>
      <div className="stack gap-4">
        <div className="field">
          <label>Endpoint URL</label>
          <input className="input mono" placeholder="https://api.your-app.com/mikaka/webhooks" value={url} onChange={e => setUrl(e.target.value)}/>
          <div className="hint">Must be HTTPS. We'll send up to 50 req/sec; deduplicate by <span className="mono">x-mk-event-id</span>.</div>
        </div>

        <div className="field">
          <label>Signing secret <span style={{ color: "var(--fg-faint)", fontWeight: 400 }}>(verify the <span className="mono">x-mk-signature</span> header)</span></label>
          <div className="row gap-2">
            <input className="input mono" readOnly value={secret} style={{ fontSize: 12 }}/>
            <button className="btn btn-outline btn-sm" onClick={() => navigator.clipboard?.writeText(secret)}><Icon name="copy" size={12}/> Copy</button>
            <button className="btn btn-ghost btn-sm">Roll</button>
          </div>
        </div>

        <div>
          <label style={{ fontSize: 13, fontWeight: 500, display: "block", marginBottom: 8 }}>Events to deliver</label>
          <div className="grid grid-2 gap-2">
            {Object.entries(events).map(([k, v]) => (
              <label key={k} className="row gap-3" style={{ cursor: "pointer", padding: "8px 12px", border: "1px solid var(--border)", borderRadius: 8, background: v ? "var(--mk-orange-50)" : "var(--bg)" }}>
                <div className={`switch ${v ? "on" : ""}`} onClick={() => setEvents(e => ({ ...e, [k]: !e[k] }))}/>
                <span className="mono" style={{ fontSize: 12.5 }}>{k}</span>
              </label>
            ))}
          </div>
        </div>

        <div className="grid grid-2 gap-3">
          <div className="field"><label>API version</label>
            <select className="input" value={version} onChange={e => setVersion(e.target.value)}>
              <option>2026-01</option><option>2025-09</option><option>2025-04</option>
            </select>
          </div>
          <div className="field"><label>Retries on 5xx / timeout</label>
            <select className="input" value={retries} onChange={e => setRetries(+e.target.value)}>
              <option value={0}>None</option><option value={3}>3 (1m, 5m, 1h)</option><option value={5}>5 (exponential, up to 24h)</option>
            </select>
          </div>
        </div>

        <div className="card card-pad" style={{ background: "var(--bg-sunken)" }}>
          <div className="row between" style={{ marginBottom: 8 }}>
            <div style={{ fontSize: 13, fontWeight: 500 }}>Send test event</div>
            <button className="btn btn-outline btn-sm" disabled={!validUrl || busy} onClick={runTest}>{busy ? "Sending…" : <><Icon name="play" size={12}/> Send</>}</button>
          </div>
          <div className="mono" style={{ fontSize: 11.5, color: "var(--fg-muted)", whiteSpace: "pre-wrap", lineHeight: 1.55 }}>
{`POST ${url || "<URL>"}
content-type: application/json
x-mk-signature: t=1730384012,v1=…
x-mk-event-id: evt_test_01HQXY…

{"type":"call.ended","data":{"id":"call_test","duration_s":42,"outcome":"booked"}}`}
          </div>
          {test && (
            <div className="row gap-2" style={{ marginTop: 10, fontSize: 12.5, color: test.ok ? "var(--mk-success)" : "var(--mk-danger)" }}>
              <Icon name={test.ok ? "check" : "alert"} size={14}/>
              <span>{test.code} · {test.ms} ms · <span className="mono">{test.body}</span></span>
            </div>
          )}
        </div>
      </div>
    </Modal>
  );
};

/* ====================================================================
   Manage drawer (right side) — when an existing connection is clicked.
   ==================================================================== */
const ManageDrawer = ({ id, onClose, onReconfigure }) => {
  const store = MK_USE();
  const toast = useToast();
  const c = store.connections.find(x => x.id === id);
  const [busy, setBusy] = useState(false);
  if (!c) return null;

  const disconnect = async () => {
    if (!confirm(`Disconnect ${c.name}? Mikaka will stop syncing immediately.`)) return;
    setBusy(true);
    try {
      await api.connections.disconnect(id);
      toast({ title: `Disconnected from ${c.name}` });
      onClose();
    } catch (err) {
      toast({ title: "Disconnect failed", body: err?.message || "Please retry.", kind: "danger" });
    } finally {
      setBusy(false);
    }
  };

  const recentSyncs = [
    { t: "12s ago",  ev: "call.ended → row appended", ok: true },
    { t: "4m ago",   ev: "call.ended → row appended", ok: true },
    { t: "11m ago",  ev: "booking.created → contact upserted", ok: true },
    { t: "32m ago",  ev: "call.ended → 503 from upstream, retried", ok: false },
    { t: "1h ago",   ev: "call.ended → row appended", ok: true },
  ];

  return ReactDOM.createPortal(
    <div className="mk-drawer-backdrop" onClick={onClose}>
      <div className="mk-drawer" onClick={e => e.stopPropagation()} style={{ width: 480 }}>
        <div className="row between" style={{ padding: "16px 20px", borderBottom: "1px solid var(--border)" }}>
          <div className="row gap-3">
            <div style={{ width: 38, height: 38, borderRadius: 8, background: "var(--bg-sunken)", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid var(--border)" }}>
              <BrandLogo id={c.logo} fallback={c.icon}/>
            </div>
            <div>
              <div style={{ fontWeight: 500 }}>{c.name}</div>
              <div className="mono" style={{ fontSize: 12, color: "var(--fg-muted)" }}>{c.acct}</div>
            </div>
          </div>
          <button className="btn btn-ghost btn-icon btn-sm" onClick={onClose}><Icon name="x"/></button>
        </div>

        <div style={{ flex: 1, overflowY: "auto", padding: 20 }}>
          <div className="row gap-2" style={{ marginBottom: 16 }}>
            {HEALTH_CHIP[c.health || "ok"]}
            <span className="chip chip-neutral">{CAT_LABEL[c.category]}</span>
            <AuthBadge auth={c.auth}/>
          </div>

          {c.health === "warn" && (
            <div className="card card-pad" style={{ background: "rgba(224,148,0,.06)", borderColor: "rgba(224,148,0,.3)", marginBottom: 16 }}>
              <div className="row gap-2" style={{ color: "var(--mk-warning)", fontWeight: 500, marginBottom: 4 }}>
                <Icon name="alert" size={14}/> Action needed
              </div>
              <div style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>
                3 timeouts in the last 30 minutes from Daraja. Most likely cause: shortcode IP whitelist hasn't been updated. <a className="dot-link">View runbook</a>.
              </div>
              <button className="btn btn-outline btn-sm" style={{ marginTop: 10 }} onClick={onReconfigure}>Reconfigure</button>
            </div>
          )}

          <div style={{ fontSize: 11.5, fontWeight: 600, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--fg-muted)", margin: "8px 0 8px" }}>Account</div>
          <div className="card" style={{ padding: 0, marginBottom: 18 }}>
            {[
              ["Connected as", c.acct || "—"],
              ["Last sync",    c.lastSync || "—"],
              ["Events · 7d",  (c.syncCount7d || 0).toLocaleString()],
              ...(c.config?.callbackUrl ? [["Callback URL", c.config.callbackUrl]] : []),
              ...(c.config?.channel    ? [["Channel",       c.config.channel]]    : []),
              ...(c.config?.tab        ? [["Tab",           c.config.tab]]        : []),
            ].map(([k, v], i, arr) => (
              <div key={i} className="row between" style={{ padding: "10px 14px", borderBottom: i < arr.length - 1 ? "1px solid var(--border)" : "none", fontSize: 13 }}>
                <span style={{ color: "var(--fg-muted)" }}>{k}</span>
                <span className="mono" style={{ fontSize: 12, maxWidth: 240, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{v}</span>
              </div>
            ))}
          </div>

          {c.scopes && (
            <>
              <div style={{ fontSize: 11.5, fontWeight: 600, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--fg-muted)", margin: "0 0 8px" }}>Scopes granted</div>
              <div className="row gap-2" style={{ flexWrap: "wrap", marginBottom: 18 }}>
                {c.scopes.map(s => <span key={s} className="chip chip-neutral mono" style={{ fontSize: 11 }}>{s}</span>)}
              </div>
            </>
          )}

          <div style={{ fontSize: 11.5, fontWeight: 600, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--fg-muted)", margin: "0 0 8px" }}>Recent activity</div>
          <div className="card" style={{ padding: 0, marginBottom: 18 }}>
            {recentSyncs.map((r, i) => (
              <div key={i} className="row gap-3" style={{ padding: "10px 14px", borderBottom: i < recentSyncs.length - 1 ? "1px solid var(--border)" : "none", fontSize: 12.5 }}>
                <span style={{ width: 6, height: 6, borderRadius: 999, background: r.ok ? "var(--mk-success)" : "var(--mk-danger)", flexShrink: 0 }}/>
                <div style={{ flex: 1 }}>{r.ev}</div>
                <div className="mono" style={{ color: "var(--fg-faint)", fontSize: 11.5 }}>{r.t}</div>
              </div>
            ))}
          </div>

          <div className="row gap-2">
            <button className="btn btn-outline btn-sm" onClick={onReconfigure}><Icon name="settings" size={12}/> Reconfigure</button>
            <button className="btn btn-outline btn-sm" disabled={busy} onClick={async () => {
              setBusy(true);
              try {
                await api.connections.refresh(id);
                toast({ title: "Sync refreshed", body: c.name });
              } catch (err) {
                toast({ title: "Sync failed", body: err?.message || "Please retry.", kind: "danger" });
              } finally {
                setBusy(false);
              }
            }}>
              <Icon name="refresh" size={12}/> {busy ? "Syncing..." : "Sync now"}
            </button>
            <div style={{ flex: 1 }}/>
            <button className="btn btn-sm" style={{ color: "var(--mk-danger)", borderColor: "var(--mk-danger)", background: "transparent", border: "1px solid var(--mk-danger)" }} disabled={busy} onClick={disconnect}>
              <Icon name="log-out" size={12}/> Disconnect
            </button>
          </div>
        </div>
      </div>
    </div>,
    document.body
  );
};

Object.assign(window, { TenantConnections });
