// ─────────────────────────────────────────────────────────────────────────
// Meridian — Settings (wired to /api/credentials, codex OAuth, AA login,
// password change, TOTP enrolment, manual job triggers)
// ─────────────────────────────────────────────────────────────────────────

const PROVIDERS = [
  { id: "finanzfluss", name: "Finanzfluss", role: "Brokerage sync", auth: "fields", icon: "mail",
    fields: [{ k: "email", label: "Email", type: "email" }, { k: "password", label: "Password", type: "password" }, { k: "totp_seed", label: "2FA seed", type: "text", mono: true }] },
  { id: "eulerpool", name: "Eulerpool", role: "AAQS + fair value", auth: "fields", icon: "key-round",
    fields: [{ k: "api_key", label: "API key", type: "password", mono: true }] },
  { id: "alleaktien_premium", name: "AlleAktien Premium", role: "Stock analyses (expiring session)", auth: "aa_premium", icon: "wand-2" },
  { id: "alleaktien_investors", name: "AlleAktien Investors", role: "Weekly calls", auth: "fields", icon: "mail",
    fields: [{ k: "email", label: "Email", type: "email" }, { k: "password", label: "Password", type: "password" }] },
  { id: "brave", name: "Brave Search", role: "Web search", auth: "fields", icon: "key-round",
    fields: [{ k: "api_key", label: "API key", type: "password", mono: true }] },
  { id: "firecrawl", name: "Firecrawl", role: "Page extraction", auth: "fields", icon: "key-round",
    fields: [{ k: "api_key", label: "API key", type: "password", mono: true }] },
  { id: "groq", name: "Groq", role: "Transcription", auth: "fields", icon: "key-round",
    fields: [{ k: "api_key", label: "API key", type: "password", mono: true }] },
  { id: "codex", name: "Codex / ChatGPT", role: "Powers Hermes", auth: "oauth", icon: "circle-user" },
];

function statusOf(row) {
  if (!row || !row.configured) return { lbl: "Not configured", cls: "plain" };
  if (row.last_test_result === "fail") return { lbl: "Error", cls: "err" };
  if (row.last_test_result === "ok" || row.status === "ok") return { lbl: "Connected", cls: "ok" };
  return { lbl: "Saved", cls: "warn" };
}

function Settings({ theme, onNavigate, user }) {
  const [tab, setTab] = useState("connections");
  const tabs = [["connections", "Connections", "plug"], ["security", "Security", "shield"], ["account", "Account", "user"], ["data", "Data", "database"]];
  return (
    <div className="rise">
      <PageHeader title="Settings" subtitle="Connections, security and data — your keys stay on your machine." />
      <Row gap={4} wrap style={{ marginBottom: 22, borderBottom: "1px solid var(--border-1)", paddingBottom: 0 }}>
        {tabs.map(([k, l, ic]) => (
          <button key={k} onClick={() => setTab(k)} style={{ display: "inline-flex", alignItems: "center", gap: 7, padding: "9px 14px", background: "none", border: 0, borderBottom: "2px solid " + (tab === k ? "var(--sage-600)" : "transparent"), color: tab === k ? "var(--fg-1)" : "var(--fg-2)", fontWeight: tab === k ? 600 : 500, fontSize: 14, marginBottom: -1, cursor: "pointer" }}>
            <Icon name={ic} size={15} style={{ color: tab === k ? "var(--sage-600)" : "var(--fg-3)" }} /> {l}
          </button>
        ))}
      </Row>
      {tab === "connections" && <ConnectionsTab />}
      {tab === "security" && <SecurityTab user={user} />}
      {tab === "account" && <AccountTab user={user} onNavigate={onNavigate} />}
      {tab === "data" && <DataTab onNavigate={onNavigate} />}
    </div>
  );
}

function ConnectionsTab() {
  const creds = useAsync(() => API.credentials(), []);
  const byId = {};
  if (creds.data && creds.data.providers) creds.data.providers.forEach((p) => { byId[p.provider] = p; });
  const okCount = PROVIDERS.filter((p) => statusOf(byId[p.id]).cls === "ok").length;
  const errCount = PROVIDERS.filter((p) => statusOf(byId[p.id]).cls === "err").length;

  if (creds.loading) return <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))", gap: 14 }}>{[0,1,2,3].map(i => <SkelCard key={i} lines={3} />)}</div>;
  if (creds.error) return <Card><EmptyState icon="cloud-off" title="Couldn't load connections" body={creds.error.message} action={<button className="btn btn-sm" onClick={creds.reload}>Retry</button>} /></Card>;

  return (
    <Col gap={16}>
      <Row gap={10} wrap style={{ fontSize: 13, color: "var(--fg-2)" }}>
        <span className="chip chip-tinted"><Icon name="check-circle-2" size={13} style={{ color: "var(--ok)" }} /> {okCount} connected</span>
        <span className="chip chip-tinted"><Icon name="alert-circle" size={13} style={{ color: "var(--err)" }} /> {errCount} error</span>
        <span className="chip chip-tinted"><Icon name="circle-dashed" size={13} style={{ color: "var(--fg-3)" }} /> {PROVIDERS.length} providers</span>
      </Row>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))", gap: 14 }}>
        {PROVIDERS.map((p) => <ConnectionCard key={p.id} def={p} row={byId[p.id]} onChange={creds.reload} />)}
      </div>
    </Col>
  );
}

function ConnectionCard({ def, row, onChange }) {
  const st = statusOf(row);
  const [editing, setEditing] = useState(false);
  const [testing, setTesting] = useState(false);
  const [msg, setMsg] = useState(null); // {kind:'ok'|'err', text}

  const test = async () => {
    setTesting(true); setMsg(null);
    try {
      const r = await API.testCredential(def.id);
      setMsg({ kind: r.ok ? "ok" : "err", text: r.message || (r.ok ? "Connection OK" : "Test failed") });
      onChange();
    } catch (e) { setMsg({ kind: "err", text: e.message }); }
    finally { setTesting(false); }
  };
  const remove = async () => {
    setMsg(null);
    try { await API.deleteCredential(def.id); onChange(); } catch (e) { setMsg({ kind: "err", text: e.message }); }
  };

  return (
    <div className="card" style={{ display: "flex", flexDirection: "column" }}>
      <Row justify="space-between" align="flex-start">
        <Row gap={12}>
          <div style={{ width: 40, height: 40, borderRadius: 11, background: "var(--bg-tint)", display: "grid", placeItems: "center", color: "var(--fg-2)", flexShrink: 0 }}><Icon name={def.icon} size={18} /></div>
          <div>
            <div style={{ fontWeight: 600, fontSize: 15 }}>{def.name}</div>
            <div className="muted" style={{ fontSize: 12.5, marginTop: 1 }}>{def.role}</div>
          </div>
        </Row>
        <span className={"badge badge-" + st.cls}>{testing ? "Testing…" : st.lbl}</span>
      </Row>

      <div style={{ marginTop: 14, borderTop: "1px solid var(--border-1)", paddingTop: 12 }}>
        {def.auth === "fields" && <FieldsAuth def={def} configured={row && row.configured} editing={editing} setEditing={setEditing} onSaved={() => { setEditing(false); onChange(); }} setMsg={setMsg} />}
        {def.auth === "oauth" && <OAuthAuth row={row} onChange={onChange} setMsg={setMsg} />}
        {def.auth === "aa_premium" && <AaPremiumAuth onChange={onChange} setMsg={setMsg} configured={row && row.configured} />}
      </div>

      {msg && (
        <Row gap={8} align="flex-start" style={{ marginTop: 12, padding: "9px 11px", borderRadius: "var(--radius-3)", background: msg.kind === "err" ? "var(--danger-bg)" : "var(--signal-bg)", color: msg.kind === "err" ? "var(--danger-fg)" : "var(--signal-fg)", fontSize: 12.5, lineHeight: 1.45 }}>
          <Icon name={msg.kind === "err" ? "alert-circle" : "check-circle-2"} size={14} style={{ marginTop: 1, flexShrink: 0 }} /> <span>{msg.text}</span>
        </Row>
      )}

      <Row justify="space-between" align="center" wrap style={{ marginTop: 14, gap: 10 }}>
        <span className="muted-2" style={{ fontSize: 11.5, whiteSpace: "nowrap" }}>{row && row.last_test_at ? "Tested " + relTime(row.last_test_at) : "Never tested"}</span>
        {def.auth !== "oauth" && (
          <Row gap={8}>
            {row && row.configured && <button className="btn btn-sm btn-ghost" onClick={remove}><Icon name="trash-2" size={12} /> Remove</button>}
            <button className="btn btn-sm" onClick={test} disabled={testing || !row || !row.configured}><Icon name={testing ? "loader" : "activity"} size={12} className={testing ? "spin" : ""} /> Test</button>
          </Row>
        )}
      </Row>
    </div>
  );
}

/* ── Auth sub-forms ───────────────────────────────────────────────────────── */
function FieldsAuth({ def, configured, editing, setEditing, onSaved, setMsg }) {
  const [vals, setVals] = useState({});
  const [saving, setSaving] = useState(false);
  const set = (k, v) => setVals((s) => ({ ...s, [k]: v }));
  const save = async () => {
    setSaving(true); setMsg(null);
    try {
      const body = {};
      def.fields.forEach((f) => { if (vals[f.k] != null && vals[f.k] !== "") body[f.k] = vals[f.k]; });
      await API.saveCredential(def.id, body);
      setVals({});
      onSaved();
      setMsg({ kind: "ok", text: "Saved" });
    } catch (e) { setMsg({ kind: "err", text: e.message }); }
    finally { setSaving(false); }
  };

  if (!editing) {
    return (
      <Col gap={0}>
        {def.fields.map((f, i) => (
          <Row key={f.k} justify="space-between" align="center" style={{ padding: "10px 0", borderTop: i ? "1px solid var(--border-1)" : 0, fontSize: 13, gap: 12 }}>
            <span className="muted" style={{ whiteSpace: "nowrap" }}>{f.label}</span>
            <span className="mono" style={{ color: "var(--fg-3)", letterSpacing: "0.06em" }}>{configured ? "•••• saved" : "— not set"}</span>
          </Row>
        ))}
        <button className="btn btn-sm" style={{ marginTop: 10, alignSelf: "flex-start" }} onClick={() => setEditing(true)}>
          <Icon name={configured ? "pencil" : "plus"} size={12} /> {configured ? "Update credentials" : "Add credentials"}
        </button>
      </Col>
    );
  }
  return (
    <Col gap={12}>
      <div className="muted-2" style={{ fontSize: 11.5 }}>Secrets are never shown again — re-enter all fields to update.</div>
      {def.fields.map((f) => (
        <div key={f.k}>
          <label className="label">{f.label}</label>
          <input className={"input" + (f.mono ? " mono" : "")} type={f.type} value={vals[f.k] || ""} onChange={(e) => set(f.k, e.target.value)} placeholder={f.label} autoComplete="off" />
        </div>
      ))}
      <Row gap={8}>
        <button className="btn btn-sm btn-primary" onClick={save} disabled={saving}>{saving ? <><Icon name="loader" size={12} className="spin" style={{ color: "#fff" }} /> Saving</> : "Save"}</button>
        <button className="btn btn-sm btn-ghost" onClick={() => setEditing(false)}>Cancel</button>
      </Row>
    </Col>
  );
}

function OAuthAuth({ row, onChange, setMsg }) {
  const connected = row && (row.status === "ok" || (row.configured && row.last_test_result === "ok")) || (row && row.configured);
  const [flow, setFlow] = useState(null); // {state,url,code}
  const [polling, setPolling] = useState(false);
  const timer = useRef(null);
  useEffect(() => () => clearInterval(timer.current), []);

  const start = async () => {
    setMsg(null);
    try {
      const r = await API.codexStart();
      setFlow(r); setPolling(true);
      timer.current = setInterval(async () => {
        try {
          const st = await API.codexStatus();
          setFlow((f) => ({ ...f, ...st }));
          if (st.state === "success") { clearInterval(timer.current); setPolling(false); setFlow(null); onChange(); setMsg({ kind: "ok", text: "Codex connected" }); }
          if (st.state === "error") { clearInterval(timer.current); setPolling(false); setMsg({ kind: "err", text: st.error || "Login failed" }); }
        } catch {}
      }, 2000);
    } catch (e) {
      if (e.status === 409 && e.data) { setFlow(e.data); setPolling(true); }
      else setMsg({ kind: "err", text: e.message });
    }
  };
  const cancel = async () => { clearInterval(timer.current); setPolling(false); try { await API.codexCancel(); } catch {} setFlow(null); };
  const disconnect = async () => { setMsg(null); try { await API.codexLogout(); onChange(); } catch (e) { setMsg({ kind: "err", text: e.message }); } };

  if (flow) {
    return (
      <Col gap={10}>
        <div className="muted" style={{ fontSize: 12.5 }}>1 · Open the verification page, 2 · enter the code, 3 · approve.</div>
        {flow.url && <a href={flow.url} target="_blank" rel="noopener" className="btn btn-sm btn-primary" style={{ alignSelf: "flex-start" }}><Icon name="external-link" size={12} style={{ color: "#fff" }} /> Open login page</a>}
        {flow.code && <Row gap={8} align="center"><span className="muted" style={{ fontSize: 12 }}>Code</span><span className="mono" style={{ fontSize: 18, fontWeight: 600, letterSpacing: "0.12em" }}>{flow.code}</span></Row>}
        <Row gap={8} align="center" style={{ fontSize: 12, color: "var(--fg-2)" }}>
          {polling && <Icon name="loader" size={12} className="spin" />} {flow.state === "waiting" ? "Waiting for approval…" : "Starting…"}
          <button className="btn btn-xs btn-ghost" onClick={cancel}>Cancel</button>
        </Row>
      </Col>
    );
  }
  return (
    <Row justify="space-between" align="center" style={{ padding: "2px 0" }}>
      <Row gap={8}>
        <Icon name={connected ? "check-circle-2" : "circle-dashed"} size={15} style={{ color: connected ? "var(--ok)" : "var(--fg-3)" }} />
        <span style={{ fontSize: 13 }}>{connected ? "Connected to ChatGPT" : "Not connected"}</span>
      </Row>
      {connected ? <button className="btn btn-xs btn-ghost" onClick={disconnect}>Disconnect</button>
        : <button className="btn btn-sm btn-primary" onClick={start}><Icon name="log-in" size={12} style={{ color: "#fff" }} /> Connect</button>}
    </Row>
  );
}

// AlleAktien Premium — the session that expires. Shows live session health
// (GET /alleaktien_premium/status), a magic-link re-auth box (POST
// /alleaktien_premium/link), and a fallback "request login code" flow
// (POST /alleaktien/login/alleaktien_premium/start → /submit).
function AaPremiumAuth({ onChange, setMsg, configured }) {
  const [email, setEmail] = useState("");
  const [link, setLink] = useState("");
  const [busy, setBusy] = useState(false);
  const [live, setLive] = useState({ state: configured ? "loading" : "idle" }); // idle|loading|live|expired|unknown
  const [linkOpen, setLinkOpen] = useState(false); // advanced magic-link fallback
  const [codeStage, setCodeStage] = useState("email"); // email | code
  const [code, setCode] = useState("");
  const [codeBusy, setCodeBusy] = useState(false);

  const checkLive = useCallback(async () => {
    setLive({ state: "loading" });
    try {
      const r = await API.aaPremiumStatus();
      setLive({ state: r && r.ok ? "live" : "expired", reason: r && r.reason });
    } catch (e) {
      setLive({ state: "unknown", reason: e.message });
    }
  }, []);

  // probe once when the card mounts, only if a session was previously set up
  useEffect(() => { if (configured) checkLive(); }, []);

  const submitLink = async () => {
    if (!link.trim()) return;
    setBusy(true); setMsg(null);
    try {
      await API.aaPremiumLink(link.trim(), email.trim() || undefined);
      setLink("");
      setMsg({ kind: "ok", text: "Premium session re-established." });
      onChange();
      checkLive();
    } catch (e) { setMsg({ kind: "err", text: e.message }); }
    finally { setBusy(false); }
  };

  const requestCode = async () => {
    setCodeBusy(true); setMsg(null);
    try { await API.aaStart("alleaktien_premium", email.trim()); setCodeStage("code"); setMsg({ kind: "ok", text: "Login code sent — check your email." }); }
    catch (e) { setMsg({ kind: "err", text: e.message }); }
    finally { setCodeBusy(false); }
  };
  const submitCode = async () => {
    setCodeBusy(true); setMsg(null);
    try { await API.aaSubmit("alleaktien_premium", code.trim()); setCode(""); setCodeStage("email"); setMsg({ kind: "ok", text: "Premium session re-established." }); onChange(); checkLive(); }
    catch (e) { setMsg({ kind: "err", text: e.message }); }
    finally { setCodeBusy(false); }
  };

  const pill = ({
    loading: { cls: "warn", icon: "loader", spin: true, text: "Checking session…" },
    live:    { cls: "ok", icon: "shield-check", text: "Session live" },
    expired: { cls: "err", icon: "shield-x", text: "Session expired" },
    unknown: { cls: "plain", icon: "help-circle", text: "Status unknown" },
    idle:    { cls: "plain", icon: "circle-dashed", text: "Not connected" },
  })[live.state] || { cls: "plain", icon: "circle-dashed", text: "Not connected" };

  return (
    <Col gap={12}>
      {/* live session health */}
      <Row justify="space-between" align="center" style={{ background: "var(--bg-sunken)", border: "1px solid var(--border-1)", borderRadius: "var(--radius-3)", padding: "8px 11px", gap: 10 }}>
        <Row gap={8} style={{ minWidth: 0 }}>
          <span className={"badge badge-" + pill.cls}><Icon name={pill.icon} size={12} className={pill.spin ? "spin" : ""} /> {pill.text}</span>
          {live.reason && live.state !== "loading" && <span className="muted-2" style={{ fontSize: 11, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", minWidth: 0 }} title={live.reason}>{live.reason}</span>}
        </Row>
        <button className="btn btn-xs btn-ghost" onClick={checkLive} disabled={live.state === "loading"} style={{ flexShrink: 0 }}><Icon name="refresh-cw" size={11} className={live.state === "loading" ? "spin" : ""} /> Check</button>
      </Row>

      {/* ── 6-digit login code: the primary (and reliable) flow ─────────────── */}
      <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.5 }}>
        {codeStage === "email"
          ? "AlleAktien signs in with a 6-digit email code. Enter your email and we’ll request a fresh code, then type it below."
          : "Enter the 6-digit code AlleAktien just emailed you (valid ~24h)."}
      </div>
      <div>
        <label className="label">Email</label>
        <input className="input" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" autoComplete="off" disabled={codeStage === "code"} />
      </div>

      {codeStage === "email" ? (
        <button className="btn btn-sm btn-primary" style={{ alignSelf: "flex-start" }} onClick={requestCode} disabled={codeBusy || !email.trim()}>
          {codeBusy ? <><Icon name="loader" size={12} className="spin" style={{ color: "#fff" }} /> Requesting code…</> : <><Icon name="mail" size={12} style={{ color: "#fff" }} /> Send me a login code</>}
        </button>
      ) : (
        <>
          <div>
            <label className="label">6-digit code</label>
            <input className="input mono" value={code} onChange={(e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6))} placeholder="123456" inputMode="numeric" maxLength={6} autoComplete="one-time-code" style={{ letterSpacing: "0.4em", fontSize: 16 }} autoFocus />
          </div>
          <Row gap={8} wrap>
            <button className="btn btn-sm btn-primary" onClick={submitCode} disabled={codeBusy || code.trim().length < 6}>{codeBusy ? <><Icon name="loader" size={12} className="spin" style={{ color: "#fff" }} /> Verifying…</> : <><Icon name="shield-check" size={12} style={{ color: "#fff" }} /> Verify code</>}</button>
            <button className="btn btn-sm btn-ghost" onClick={requestCode} disabled={codeBusy}><Icon name="refresh-cw" size={12} /> Resend code</button>
            <button className="btn btn-sm btn-ghost" onClick={() => { setCodeStage("email"); setCode(""); }}>Use a different email</button>
          </Row>
        </>
      )}

      {/* ── magic link: rare fallback, hidden by default ───────────────────── */}
      <button className="btn btn-xs btn-ghost" style={{ alignSelf: "flex-start", marginTop: 2 }} onClick={() => setLinkOpen((v) => !v)}>
        <Icon name={linkOpen ? "chevron-up" : "chevron-down"} size={11} /> {linkOpen ? "Hide" : "Advanced: paste a login link instead"}
      </button>
      {linkOpen && (
        <Col gap={10} style={{ borderTop: "1px solid var(--border-1)", paddingTop: 12 }}>
          <div className="muted-2" style={{ fontSize: 11.5, lineHeight: 1.5 }}>Only if the email still includes a “Jetzt einloggen” link. Links are single-use and often already consumed — the code above is more reliable.</div>
          <div><label className="label">Login link</label><input className="input mono" value={link} onChange={(e) => setLink(e.target.value)} placeholder="https://…/CL0/…" autoComplete="off" /></div>
          <button className="btn btn-sm" style={{ alignSelf: "flex-start" }} onClick={submitLink} disabled={busy || !link.trim()}>{busy ? <><Icon name="loader" size={12} className="spin" /> Authenticating</> : <><Icon name="link" size={12} /> Use link</>}</button>
        </Col>
      )}
    </Col>
  );
}

/* ── Security ─────────────────────────────────────────────────────────────── */
function SecurityTab({ user }) {
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))", gap: 16, alignItems: "start" }}>
      <ChangePasswordCard />
      <TwoFactorCard user={user} />
    </div>
  );
}

function ChangePasswordCard() {
  const [cur, setCur] = useState(""); const [next, setNext] = useState(""); const [conf, setConf] = useState("");
  const [busy, setBusy] = useState(false); const [msg, setMsg] = useState(null);
  const submit = async (e) => {
    e.preventDefault(); setMsg(null);
    if (next.length < 8) return setMsg({ kind: "err", text: "New password must be at least 8 characters." });
    if (next !== conf) return setMsg({ kind: "err", text: "New passwords don't match." });
    setBusy(true);
    try { await API.changePassword(cur, next); setMsg({ kind: "ok", text: "Password updated. Other sessions were signed out." }); setCur(""); setNext(""); setConf(""); }
    catch (ex) { setMsg({ kind: "err", text: ex.data && ex.data.error === "invalid_current" ? "Current password is incorrect." : ex.message }); }
    finally { setBusy(false); }
  };
  return (
    <Card>
      <SectionTitle icon="key-round">Change password</SectionTitle>
      <form onSubmit={submit}>
        <Col gap={14}>
          <div><label className="label">Current password</label><input className="input" type="password" value={cur} onChange={(e) => setCur(e.target.value)} placeholder="••••••••" autoComplete="current-password" /></div>
          <div><label className="label">New password</label><input className="input" type="password" value={next} onChange={(e) => setNext(e.target.value)} placeholder="At least 8 characters" autoComplete="new-password" /></div>
          <div><label className="label">Confirm new password</label><input className="input" type="password" value={conf} onChange={(e) => setConf(e.target.value)} placeholder="Repeat new password" autoComplete="new-password" /></div>
          {msg && <Row gap={8} style={{ background: msg.kind === "err" ? "var(--danger-bg)" : "var(--signal-bg)", color: msg.kind === "err" ? "var(--danger-fg)" : "var(--signal-fg)", padding: "9px 12px", borderRadius: "var(--radius-3)", fontSize: 12.5 }}><Icon name={msg.kind === "err" ? "alert-circle" : "check-circle-2"} size={14} /> {msg.text}</Row>}
          <button type="submit" className="btn btn-primary" style={{ alignSelf: "flex-start" }} disabled={busy || !cur || !next}>{busy ? "Updating…" : "Update password"}</button>
        </Col>
      </form>
    </Card>
  );
}

function TwoFactorCard({ user }) {
  const enabled = user && user.totp_enabled;
  const [stage, setStage] = useState("idle"); // idle | password | show | done
  const [pw, setPw] = useState(""); const [secret, setSecret] = useState(null); const [qr, setQr] = useState(null);
  const [token, setToken] = useState(""); const [busy, setBusy] = useState(false); const [msg, setMsg] = useState(null);

  const begin = async () => {
    setBusy(true); setMsg(null);
    try {
      const r = await API.totpSetup({ password: pw });
      setSecret(r.secret); setQr(r.qr); setStage("show");
    } catch (ex) { setMsg({ kind: "err", text: ex.data && ex.data.error === "reauth_required" ? "Password incorrect." : ex.message }); }
    finally { setBusy(false); }
  };
  const enable = async () => {
    setBusy(true); setMsg(null);
    try { await API.totpEnable(token.trim()); setStage("done"); setMsg({ kind: "ok", text: "Two-factor authentication enabled. Reload to refresh status." }); }
    catch (ex) { setMsg({ kind: "err", text: ex.data && ex.data.error === "invalid_token" ? "That code wasn't accepted." : ex.message }); }
    finally { setBusy(false); }
  };

  return (
    <Card>
      <SectionTitle icon="shield-check">Two-factor authentication</SectionTitle>
      {enabled && stage === "idle" ? (
        <Row gap={8} style={{ padding: "10px 12px", background: "var(--signal-bg)", color: "var(--signal-fg)", borderRadius: "var(--radius-3)", fontSize: 12.5 }}><Icon name="shield-check" size={14} /> Enabled · a 6-digit code is required at sign-in.</Row>
      ) : stage === "idle" ? (
        <Col gap={12}>
          <div className="muted" style={{ fontSize: 12.5 }}>Require a 6-digit authenticator code at sign-in. Strongly recommended.</div>
          <button className="btn btn-sm btn-primary" style={{ alignSelf: "flex-start" }} onClick={() => setStage("password")}><Icon name="shield-plus" size={12} style={{ color: "#fff" }} /> Set up 2FA</button>
        </Col>
      ) : stage === "password" ? (
        <Col gap={12}>
          <div className="muted" style={{ fontSize: 12.5 }}>Confirm your password to begin enrolment.</div>
          <input className="input" type="password" value={pw} onChange={(e) => setPw(e.target.value)} placeholder="Current password" autoComplete="current-password" />
          <Row gap={8}><button className="btn btn-sm btn-primary" onClick={begin} disabled={busy || !pw}>{busy ? "Checking…" : "Continue"}</button><button className="btn btn-sm btn-ghost" onClick={() => setStage("idle")}>Cancel</button></Row>
        </Col>
      ) : stage === "show" ? (
        <Col gap={12}>
          <div className="muted" style={{ fontSize: 12.5 }}>Scan with your authenticator app, then enter the current code.</div>
          {qr && <img src={qr} alt="2FA QR" style={{ width: 160, height: 160, borderRadius: 10, border: "1px solid var(--border-1)", background: "#fff" }} />}
          {secret && <div className="mono" style={{ fontSize: 12, color: "var(--fg-2)", wordBreak: "break-all" }}>{secret}</div>}
          <input className="input mono" value={token} onChange={(e) => setToken(e.target.value)} placeholder="123456" />
          <Row gap={8}><button className="btn btn-sm btn-primary" onClick={enable} disabled={busy || token.length < 6}>{busy ? "Enabling…" : "Enable"}</button><button className="btn btn-sm btn-ghost" onClick={() => setStage("idle")}>Cancel</button></Row>
        </Col>
      ) : (
        <Row gap={8} style={{ padding: "10px 12px", background: "var(--signal-bg)", color: "var(--signal-fg)", borderRadius: "var(--radius-3)", fontSize: 12.5 }}><Icon name="check-circle-2" size={14} /> Enabled.</Row>
      )}
      {msg && <Row gap={8} style={{ marginTop: 12, background: msg.kind === "err" ? "var(--danger-bg)" : "var(--signal-bg)", color: msg.kind === "err" ? "var(--danger-fg)" : "var(--signal-fg)", padding: "9px 12px", borderRadius: "var(--radius-3)", fontSize: 12.5 }}><Icon name={msg.kind === "err" ? "alert-circle" : "check-circle-2"} size={14} /> {msg.text}</Row>}
    </Card>
  );
}

/* ── Account ──────────────────────────────────────────────────────────────── */
function AccountTab({ user, onNavigate }) {
  const email = (user && user.email) || "—";
  const initials = email !== "—" ? email.slice(0, 2).toUpperCase() : "··";
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))", gap: 16, alignItems: "start" }}>
      <Card>
        <SectionTitle icon="user">Profile</SectionTitle>
        <Row gap={14} style={{ marginBottom: 16 }}>
          <Avatar initials={initials} size="lg" gradient="linear-gradient(140deg, var(--sage-500), var(--sage-700))" />
          <div><div style={{ fontWeight: 600, fontSize: 16 }}>{email.split("@")[0]}</div><div className="muted" style={{ fontSize: 13 }}>Self-hosted · single user</div></div>
        </Row>
        <Col gap={0}>
          <Row justify="space-between" style={{ padding: "10px 0", fontSize: 13.5 }}><span className="muted">Login email</span><span className="mono" style={{ fontWeight: 500 }}>{email}</span></Row>
          {user && user.created_at && <Row justify="space-between" style={{ padding: "10px 0", borderTop: "1px solid var(--border-1)", fontSize: 13.5 }}><span className="muted">Member since</span><span className="mono">{fmt.date(user.created_at)}</span></Row>}
          {user && user.last_login_at && <Row justify="space-between" style={{ padding: "10px 0", borderTop: "1px solid var(--border-1)", fontSize: 13.5 }}><span className="muted">Last login</span><span className="mono">{relTime(user.last_login_at)}</span></Row>}
        </Col>
      </Card>
      <Card>
        <SectionTitle icon="palette">Preferences</SectionTitle>
        <Col gap={0}>
          {[["Base currency", "EUR (€)"], ["Number format", "1.234,56"], ["Date format", "TT.MM.JJJJ"], ["Theme", "Top-bar toggle"]].map(([k, v], i) => (
            <Row key={k} justify="space-between" align="center" style={{ padding: "10px 0", borderTop: i ? "1px solid var(--border-1)" : 0, fontSize: 13.5 }}>
              <span className="muted">{k}</span><span style={{ fontWeight: 500 }} className="mono">{v}</span>
            </Row>
          ))}
        </Col>
      </Card>
    </div>
  );
}

/* ── Data / manual jobs ───────────────────────────────────────────────────── */
function DataTab({ onNavigate }) {
  const jobs = [
    { title: "Refresh portfolio", desc: "Pull latest positions & cash from Finanzfluss", icon: "wallet", run: () => API.refreshPortfolio() },
    { title: "Rebuild recommendations", desc: "Re-score every holding on quality & value", icon: "git-fork", run: () => API.recommendationsRebuild() },
    { title: "Check for new papers", desc: "Fetch new AlleAktien analyses", icon: "file-text", run: () => API.papersCheck() },
    { title: "Re-index knowledge base", desc: "Re-embed the vault for semantic search", icon: "boxes", run: () => API.obsidianReindex() },
    { title: "Poll news", desc: "Scan for material events on your holdings", icon: "newspaper", run: () => API.newsPoll() },
  ];
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))", gap: 16, alignItems: "start" }}>
      <Card>
        <SectionTitle icon="refresh-cw">Manual refresh</SectionTitle>
        <Col gap={2}>
          {jobs.map((j, i) => <RefreshRow key={j.title} {...j} top={i > 0} />)}
        </Col>
      </Card>
      <Card style={{ background: "var(--bg-sunken)" }}>
        <Row justify="space-between" align="center">
          <Row gap={10}><Icon name="activity" size={16} style={{ color: "var(--fg-2)" }} /><div><div style={{ fontWeight: 600, fontSize: 13.5 }}>Background jobs</div><div className="muted" style={{ fontSize: 12 }}>See health & last-run for every scheduled job</div></div></Row>
          <button className="btn btn-sm" onClick={() => onNavigate("system")}>System status <Icon name="arrow-right" size={12} /></button>
        </Row>
      </Card>
    </div>
  );
}

function RefreshRow({ title, desc, icon, run, top }) {
  const [state, setState] = useState("idle"); // idle | running | done | error
  const go = async () => {
    setState("running");
    try { await run(); setState("done"); setTimeout(() => setState("idle"), 2600); }
    catch { setState("error"); setTimeout(() => setState("idle"), 3000); }
  };
  return (
    <Row justify="space-between" align="center" style={{ padding: "12px 0", borderTop: top ? "1px solid var(--border-1)" : 0 }}>
      <Row gap={11}>
        <div style={{ width: 32, height: 32, borderRadius: 9, background: "var(--bg-tint)", display: "grid", placeItems: "center", color: "var(--fg-2)", flexShrink: 0 }}><Icon name={icon} size={15} /></div>
        <div><div style={{ fontWeight: 600, fontSize: 13.5 }}>{title}</div><div className="muted" style={{ fontSize: 12 }}>{desc}</div></div>
      </Row>
      <button className="btn btn-sm" onClick={go} disabled={state === "running"} style={{ minWidth: 100, justifyContent: "center" }}>
        {state === "running" ? <><Icon name="loader" size={12} className="spin" /> Running</>
          : state === "done" ? <><Icon name="check" size={13} style={{ color: "var(--ok)" }} /> Done</>
          : state === "error" ? <><Icon name="alert-circle" size={13} style={{ color: "var(--err)" }} /> Failed</>
          : <><Icon name="play" size={12} /> Run now</>}
      </button>
    </Row>
  );
}

Object.assign(window, { Settings, ConnectionCard });
