// API Keys / Environment — the secrets-and-credentials plane that mirrors the
// Hermes Agent dashboard's Environment page: editable env vars grouped by category
// (channel-managed ones hidden, advanced behind a toggle), the read-only credential
// pool (per-provider key rotation), and the read-only OAuth providers (device-code
// CLI flows). Wired to HERMES (envVars/envSet/envDelete/envReveal, credentialsPool,
// providersOAuth) with a graceful empty state so the standalone preview stays rendered.
const { useState, useEffect, useCallback, useRef } = React;

// Human labels for the env-var categories the backend emits. Unknown categories
// fall through to a title-cased version of the raw key.
const CAT_LABEL = {
  provider: "Provider keys",
  tool: "Tool integrations",
  messaging: "Messaging platforms",
  system: "System",
  other: "Other",
};
const catLabel = (c) => CAT_LABEL[c] || (c ? c.charAt(0).toUpperCase() + c.slice(1) : "Other");

// Group the flat env-vars object into ordered [category, [[key,info], ...]] entries.
// Hides channel-managed vars entirely; advanced vars are kept (the row list filters
// them by the Show-advanced toggle so a category never disappears unexpectedly).
function groupEnv(vars) {
  const groups = {};
  Object.entries(vars || {}).forEach(([key, info]) => {
    if (!info || info.channel_managed === true) return;
    const cat = info.category || "other";
    (groups[cat] = groups[cat] || []).push([key, info]);
  });
  Object.values(groups).forEach((list) => list.sort((a, b) => a[0].localeCompare(b[0])));
  // Stable category order: known categories first (in CAT_LABEL order), then the rest.
  const known = Object.keys(CAT_LABEL).filter((c) => groups[c]);
  const rest = Object.keys(groups).filter((c) => !CAT_LABEL[c]).sort();
  return [...known, ...rest].map((c) => [c, groups[c]]);
}

// =====================================================================
// Inline editor for one env var. Set/Update opens this in place of the
// status tag; Save -> envSet(key,value). Password vars use type=password.
// =====================================================================
function EnvEditor({ info, busy, onSave, onCancel }) {
  const [value, setValue] = useState("");
  const ref = useRef(null);
  useEffect(() => { if (ref.current) ref.current.focus(); }, []);
  return (
    <div className="flex" style={{ gap: 6, alignItems: "center", flex: 1, justifyContent: "flex-end", maxWidth: 360 }}>
      <input
        ref={ref}
        className="input mono"
        type={info.is_password ? "password" : "text"}
        placeholder={info.is_set ? "New value…" : "Enter value…"}
        value={value}
        disabled={busy}
        style={{ flex: 1, minWidth: 0 }}
        onChange={(e) => setValue(e.target.value)}
        onKeyDown={(e) => { if (e.key === "Enter" && value) onSave(value); if (e.key === "Escape") onCancel(); }}
      />
      <button className="btn btn-primary btn-sm" disabled={busy || !value} onClick={() => onSave(value)}>
        {busy ? "Saving…" : "Save"}
      </button>
      <button className="icon-btn" title="Cancel" disabled={busy} onClick={onCancel}><Icon name="x" size={16} /></button>
    </div>
  );
}

// One env-var row: name (mono, lock if is_password), description (faint + external
// link), and a right side that swaps between status tag, inline editor, and actions.
function EnvRow({ name, info, busy, editing, revealed, onEdit, onCancel, onSave, onReveal, onDelete }) {
  return (
    <div className="row" style={{ cursor: "default", alignItems: "flex-start" }}>
      <span className="glyph"><Icon name={info.is_password ? "lock" : "sliders"} size={15} /></span>
      <div className="row-main" style={{ minWidth: 0 }}>
        <div className="flex" style={{ gap: 6, alignItems: "center", flexWrap: "wrap" }}>
          <b className="mono" style={{ fontSize: 13 }}>{name}</b>
          {info.is_password && <Icon name="lock" size={12} className="faint" />}
        </div>
        {info.description && (
          <div className="row-sub" style={{ marginTop: 3 }}>
            <span className="faint">{info.description}</span>
            {info.url && (
              <a className="icon-btn" href={info.url} target="_blank" rel="noreferrer" title="Open docs" style={{ marginLeft: 6, display: "inline-flex" }}>
                <Icon name="external" size={13} />
              </a>
            )}
          </div>
        )}
        {revealed != null && (
          <div className="row-sub mono" style={{ marginTop: 4, color: "var(--primary)", wordBreak: "break-all" }}>
            <span>{revealed || "(empty)"}</span>
          </div>
        )}
      </div>

      {editing ? (
        <EnvEditor info={info} busy={busy} onSave={onSave} onCancel={onCancel} />
      ) : (
        <div className="flex" style={{ gap: 6, alignItems: "center" }}>
          {info.is_set
            ? <span className="tag mono" title="current value (redacted)">{info.redacted_value || "set"}</span>
            : <span className="faint" style={{ fontSize: 12.5 }}>not set</span>}
          {info.is_set && (
            <button className="icon-btn" title="Reveal value" disabled={busy} onClick={onReveal}><Icon name="search" size={15} /></button>
          )}
          <button className="btn btn-ghost btn-sm" disabled={busy} onClick={onEdit}>
            <Icon name="edit" size={13} />{info.is_set ? "Update" : "Set"}
          </button>
          {info.is_set && (
            <button className="btn btn-danger btn-sm" disabled={busy} onClick={onDelete}>
              <Icon name="trash" size={13} />Remove
            </button>
          )}
        </div>
      )}
    </div>
  );
}

function PageKeys({ toast }) {
  const [vars, setVars] = useState({});          // { VAR:{is_set, redacted_value, ...} }
  const [pool, setPool] = useState([]);          // credentialsPool().providers
  const [oauth, setOauth] = useState([]);        // providersOAuth().providers
  const [live, setLive] = useState(null);        // null=loading, true=backend, false=offline
  const [showAdvanced, setShowAdvanced] = useState(false);
  const [editing, setEditing] = useState(null);  // key currently in inline-edit mode
  const [busyKey, setBusyKey] = useState(null);  // key currently mutating
  const [revealed, setRevealed] = useState({});  // { key: plaintext } shown transiently
  const revealTimers = useRef({});

  const load = useCallback(async () => {
    let anyLive = false;
    try { const e = await HERMES.envVars(); setVars(e || {}); anyLive = true; }
    catch { setVars({}); }
    try { const c = await HERMES.credentialsPool(); setPool(Array.isArray(c?.providers) ? c.providers : []); anyLive = true; }
    catch { setPool([]); }
    try { const o = await HERMES.providersOAuth(); setOauth(Array.isArray(o?.providers) ? o.providers : []); anyLive = true; }
    catch { setOauth([]); }
    setLive(anyLive);
  }, []);

  useEffect(() => { load(); }, [load]);
  // Clear any pending reveal timers on unmount.
  useEffect(() => () => { Object.values(revealTimers.current).forEach(clearTimeout); }, []);

  // Save a value for one key, then reload + toast.
  const save = useCallback(async (key, value) => {
    setBusyKey(key);
    try {
      await HERMES.envSet(key, value);
      toast(`${key} saved`);
      setEditing(null);
      await load();
    } catch (e) { toast(e.message || "Failed to save"); }
    setBusyKey(null);
  }, [load, toast]);

  // Remove a key (with confirm), then reload + toast.
  const remove = useCallback(async (key) => {
    if (!window.confirm(`Remove ${key}? This deletes the stored value.`)) return;
    setBusyKey(key);
    try {
      await HERMES.envDelete(key);
      toast(`${key} removed`);
      await load();
    } catch (e) { toast(e.message || "Failed to remove"); }
    setBusyKey(null);
  }, [load, toast]);

  // Reveal the plaintext for ~10s, then auto-hide.
  const reveal = useCallback(async (key) => {
    setBusyKey(key);
    try {
      const r = await HERMES.envReveal(key);
      const plain = typeof r === "string" ? r : (r && (r.value ?? r.plaintext)) || "";
      setRevealed((m) => ({ ...m, [key]: plain }));
      clearTimeout(revealTimers.current[key]);
      revealTimers.current[key] = setTimeout(() => {
        setRevealed((m) => { const n = { ...m }; delete n[key]; return n; });
      }, 10000);
      toast(`${key} revealed — hides in 10s`);
    } catch (e) { toast(e.message || "Failed to reveal"); }
    setBusyKey(null);
  }, [toast]);

  const groups = groupEnv(vars);
  // Visible var count after the advanced filter (channel-managed already excluded).
  const visibleCount = groups.reduce((n, [, list]) =>
    n + list.filter(([, info]) => showAdvanced || !info.advanced).length, 0);
  const hasAdvanced = Object.values(vars).some((info) => info && info.advanced && info.channel_managed !== true);

  return (
    <div className="page">
      <PageHead eyebrow="Workspace" title="API Keys" sub="Environment variables, the credential pool, and OAuth providers for Hermes Agent.">
        {live === true && <span className="badge ok"><span className="d" />live</span>}
        {live === false && <span className="badge warn"><span className="d" />preview data</span>}
        {live === null && <span className="badge"><span className="d" />loading…</span>}
        <button className="btn btn-ghost" onClick={load}><Icon name="refresh" size={16} />Refresh</button>
      </PageHead>

      {/* ---- Environment variables ---- */}
      <div className="card" style={{ marginBottom: "var(--gap)" }}>
        <div className="card-head">
          <div>
            <h2>Environment variables</h2>
            <p>Secrets and integration keys, grouped by category. Channel-managed values are hidden.</p>
          </div>
          {hasAdvanced && (
            <div className="flex" style={{ gap: 8, alignItems: "center" }}>
              <span className="faint mono" style={{ fontSize: 12 }}>Show advanced</span>
              <Toggle on={showAdvanced} onChange={setShowAdvanced} />
            </div>
          )}
        </div>

        {!visibleCount ? (
          <div className="empty">No environment variables to configure.</div>
        ) : (
          groups.map(([cat, list]) => {
            const rows = list.filter(([, info]) => showAdvanced || !info.advanced);
            if (!rows.length) return null;
            return (
              <div key={cat} style={{ marginBottom: 14 }}>
                <div className="flex between" style={{ margin: "4px 2px 8px" }}>
                  <span className="section-title">{catLabel(cat)}</span>
                  <span className="faint mono" style={{ fontSize: 11.5 }}>{rows.length}</span>
                </div>
                <div className="rows">
                  {rows.map(([key, info]) => (
                    <EnvRow
                      key={key}
                      name={key}
                      info={info}
                      busy={busyKey === key}
                      editing={editing === key}
                      revealed={key in revealed ? revealed[key] : null}
                      onEdit={() => setEditing(key)}
                      onCancel={() => setEditing(null)}
                      onSave={(v) => save(key, v)}
                      onReveal={() => reveal(key)}
                      onDelete={() => remove(key)}
                    />
                  ))}
                </div>
              </div>
            );
          })
        )}
      </div>

      {/* ---- Credential pool (read-only) ---- */}
      <div className="card" style={{ marginBottom: "var(--gap)" }}>
        <div className="card-head">
          <div>
            <h2>Credential pool</h2>
            <p>Rotating keys per provider — priority, source, and request counts. Read-only.</p>
          </div>
          <span className="faint mono" style={{ fontSize: 12 }}>
            {pool.length} provider{pool.length === 1 ? "" : "s"}
          </span>
        </div>
        {!pool.length ? (
          <div className="empty">No pooled credentials configured.</div>
        ) : (
          pool.map((p) => (
            <div key={p.provider} style={{ marginBottom: 14 }}>
              <div className="flex between" style={{ margin: "4px 2px 8px" }}>
                <span className="section-title" style={{ textTransform: "capitalize" }}>{p.provider}</span>
                <span className="faint mono" style={{ fontSize: 11.5 }}>{(p.entries || []).length}</span>
              </div>
              {!(p.entries || []).length ? (
                <div className="empty">No entries.</div>
              ) : (
                <div className="rows">
                  {p.entries.map((en) => (
                    <div className="row" key={en.id ?? en.index} style={{ cursor: "default", alignItems: "flex-start" }}>
                      <span className="glyph"><Icon name="lock" size={15} /></span>
                      <div className="row-main">
                        <div className="flex" style={{ gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                          <b style={{ fontSize: 13.5 }}>{en.label || `Key ${en.index}`}</b>
                          {en.source && <span className="tag">{en.source}</span>}
                          {en.auth_type && <span className="tag">{en.auth_type}</span>}
                          {en.has_refresh && <span className="tag" title="has refresh token">refresh</span>}
                          {en.last_status && (
                            <span className={cls("badge", String(en.last_status).toLowerCase().includes("ok") || String(en.last_status).toLowerCase().includes("active") ? "ok" : "warn")}>
                              <span className="d" />{en.last_status}
                            </span>
                          )}
                        </div>
                        <div className="row-sub mono" style={{ marginTop: 4 }}>
                          <span>{en.token_preview || "—"}</span>
                          <span> · priority {en.priority ?? "—"}</span>
                          <span> · {en.request_count ?? 0} req</span>
                        </div>
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          ))
        )}
      </div>

      {/* ---- OAuth providers (read-only) ---- */}
      <div className="card">
        <div className="card-head">
          <div>
            <h2>OAuth providers</h2>
            <p>Device-code CLI logins. Run the command in your terminal to connect — these are display-only here.</p>
          </div>
          <span className="faint mono" style={{ fontSize: 12 }}>
            {oauth.length} provider{oauth.length === 1 ? "" : "s"}
          </span>
        </div>
        {!oauth.length ? (
          <div className="empty">No OAuth providers available.</div>
        ) : (
          <div className="rows">
            {oauth.map((p) => {
              const st = p.status || {};
              return (
                <div className="row" key={p.id} style={{ cursor: "default", alignItems: "flex-start" }}>
                  <span className="glyph"><Icon name="providers" size={15} /></span>
                  <div className="row-main">
                    <div className="flex" style={{ gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                      <b style={{ fontSize: 14 }}>{p.name || p.id}</b>
                      {p.flow && <span className="tag">{p.flow}</span>}
                      {st.logged_in
                        ? <span className="badge ok"><span className="d" />connected</span>
                        : <span className="badge warn"><span className="d" />not connected</span>}
                      {st.source_label && <span className="faint mono" style={{ fontSize: 11.5 }}>{st.source_label}</span>}
                    </div>
                    {st.token_preview && (
                      <div className="row-sub mono" style={{ marginTop: 4 }}>
                        <span>{st.token_preview}</span>
                        {st.has_refresh_token && <span> · refresh</span>}
                        {st.expires_at && <span> · expires {st.expires_at}</span>}
                      </div>
                    )}
                    {p.cli_command && (
                      <div className="row-sub" style={{ marginTop: 4 }}>
                        <span className="faint">{st.logged_in ? "Re-auth" : "Connect"} via terminal: </span>
                        <span className="mono" style={{ color: "var(--primary)" }}>{p.cli_command}</span>
                      </div>
                    )}
                  </div>
                  {p.docs_url && (
                    <a className="icon-btn" href={p.docs_url} target="_blank" rel="noreferrer" title="Open docs"><Icon name="external" size={15} /></a>
                  )}
                </div>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
}
window.PageKeys = PageKeys;
