// 系统设置：admin 后台管理 system_configs（仅 roleId=1）
// 后端 /api/admin/configs，权限闸：require_admin (role_id==1 或 BOOTSTRAP_ADMIN_USER_IDS)

const CATEGORY_META = {
  business_credentials: { label: "业务凭据", desc: "敏感字段；Fernet 加密落库；改完即时生效" },
  external_apis:        { label: "第三方接口", desc: "外部 API 的 base URL / path；改完 ≤30s 缓存生效" },
  business_params:      { label: "业务参数", desc: "运行时可调；改完 ≤30s 缓存生效" },
  storage:              { label: "S3 / 对象存储", desc: "前端拿到的对象 URL 基址（公开链接）" },
  cors:                 { label: "CORS / 安全",   desc: "⚠️ 改 CORS 后需要重启容器才能生效" },
};
const CATEGORY_ORDER = ["business_credentials", "external_apis", "business_params", "storage", "cors"];

function _isAdmin() {
  const u = Auth.getUser() || {};
  return u.roleId === 1;
}

function _fmtTime(iso) {
  if (!iso) return "";
  try {
    const d = new Date(iso);
    if (isNaN(d.getTime())) return iso;
    const pad = (n) => String(n).padStart(2, "0");
    return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
  } catch (_) { return iso; }
}

const AdminConfigsPage = () => {
  // 前端兜底：非 admin 直接 403 占位（后端也会拒，但这里给个友好提示）
  if (!_isAdmin()) {
    return (
      <div className="page">
        <Topbar crumbs={["系统", "系统设置"]} />
        <div style={{padding: 48, maxWidth: 640, color: "var(--fg-2)"}}>
          <div style={{fontSize: 18, fontWeight: 600, marginBottom: 10, color: "var(--fg-1)"}}>403 仅管理员可访问</div>
          <div style={{fontSize: 13, lineHeight: 1.7}}>
            当前账号 roleId 不为 1。请联系运维设置管理员角色，或临时通过容器
            <code style={{padding: "1px 6px", background: "var(--bg-2)", borderRadius: 3, margin: "0 4px"}}>BOOTSTRAP_ADMIN_USER_IDS</code>
            env 加入白名单。
          </div>
        </div>
      </div>
    );
  }

  const [items, setItems]       = React.useState([]);
  const [loading, setLoading]   = React.useState(true);
  const [error, setError]       = React.useState("");
  const [draft, setDraft]       = React.useState({});   // {key: newValue} 未保存
  const [revealed, setRevealed] = React.useState({});   // {key: plaintext} 已解密
  const [saving, setSaving]     = React.useState({});   // {key: true} 正在保存
  const [flash, setFlash]       = React.useState({});   // {key: "saved"|"error"} 短暂提示

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

  async function reload() {
    setLoading(true); setError("");
    try {
      const r = await API.admin.configs.list();
      setItems(r.items || []);
    } catch (e) {
      setError(String(e.message || e));
    } finally {
      setLoading(false);
    }
  }

  async function onReveal(key) {
    try {
      const r = await API.admin.configs.reveal(key);
      setRevealed((s) => ({ ...s, [key]: r.value || "" }));
      setDraft((s) => ({ ...s, [key]: r.value || "" }));
    } catch (e) {
      setError(`显示 ${key} 失败：${e.message || e}`);
    }
  }

  function onHide(key) {
    setRevealed((s) => { const x = { ...s }; delete x[key]; return x; });
    setDraft((s) => { const x = { ...s }; delete x[key]; return x; });
  }

  async function onSave(item) {
    const v = draft[item.key] ?? "";
    setSaving((s) => ({ ...s, [item.key]: true }));
    setError("");
    try {
      await API.admin.configs.update(item.key, v);
      setFlash((s) => ({ ...s, [item.key]: "saved" }));
      setTimeout(() => setFlash((s) => { const x = { ...s }; delete x[item.key]; return x; }), 2400);
      await reload();
      // 保存后默认重新 mask（除非用户还在显示状态）
      if (item.is_secret && !revealed[item.key]) {
        setDraft((s) => { const x = { ...s }; delete x[item.key]; return x; });
      }
    } catch (e) {
      setError(`保存 ${item.key} 失败：${e.message || e}`);
      setFlash((s) => ({ ...s, [item.key]: "error" }));
      setTimeout(() => setFlash((s) => { const x = { ...s }; delete x[item.key]; return x; }), 3000);
    } finally {
      setSaving((s) => { const x = { ...s }; delete x[item.key]; return x; });
    }
  }

  const grouped = React.useMemo(() => {
    const m = {};
    for (const it of items) {
      const c = it.category || "general";
      if (!m[c]) m[c] = [];
      m[c].push(it);
    }
    return m;
  }, [items]);

  return (
    <div className="page">
      <Topbar crumbs={["系统", "系统设置"]}
        right={<button className="btn" onClick={reload} disabled={loading}>{loading ? "加载..." : "刷新"}</button>}
      />
      <div style={{padding: "20px 28px", maxWidth: 1080}}>
        {error && (
          <div style={{
            padding: "10px 14px", marginBottom: 16,
            background: "color-mix(in oklch, #ef4444 8%, var(--bg-1))",
            border: "1px solid #ef4444", borderRadius: 6, color: "#ef4444", fontSize: 12,
          }}>{error}</div>
        )}

        <div style={{marginBottom: 18, fontSize: 12, color: "var(--fg-3)", lineHeight: 1.7}}>
          运行时配置中心。改动落库后通过 30s in-memory 缓存广播，consumer 自动热生效（除 CORS）。
          所有 secret 走 Fernet 加密；DB 行清空 → 自动回退到容器 env。
        </div>

        {loading && items.length === 0 && (
          <div style={{color: "var(--fg-3)", padding: 40, textAlign: "center"}}>加载中…</div>
        )}

        {CATEGORY_ORDER.map((cat) => {
          const list = grouped[cat];
          if (!list || list.length === 0) return null;
          const meta = CATEGORY_META[cat] || { label: cat, desc: "" };
          return (
            <section key={cat} style={{marginBottom: 28}}>
              <div style={{
                display: "flex", alignItems: "baseline", gap: 12,
                padding: "8px 0", borderBottom: "1px solid var(--line-1)", marginBottom: 12,
              }}>
                <div style={{fontSize: 14, fontWeight: 600, color: "var(--fg-1)"}}>{meta.label}</div>
                <div style={{fontSize: 11, color: "var(--fg-3)"}}>{meta.desc}</div>
              </div>

              <div style={{display: "flex", flexDirection: "column", gap: 10}}>
                {list.map((it) => {
                  const isRevealed = it.key in revealed;
                  const draftVal = draft[it.key];
                  // 显示用：secret 未 reveal 时显示 mask；reveal 后或非 secret 都用 draft（或当前 value）
                  const displayValue = it.is_secret && !isRevealed
                    ? (draftVal !== undefined ? draftVal : (it.has_value ? "********" : ""))
                    : (draftVal !== undefined ? draftVal : (it.value || ""));
                  const dirty = draftVal !== undefined && draftVal !== (it.is_secret && !isRevealed ? (it.has_value ? "********" : "") : (it.value || ""));
                  const isSaving = !!saving[it.key];
                  const flashType = flash[it.key];

                  return (
                    <div key={it.key} style={{
                      padding: "12px 14px",
                      background: "var(--bg-1)",
                      border: "1px solid var(--line-1)",
                      borderRadius: 6,
                    }}>
                      <div style={{display: "flex", alignItems: "center", gap: 10, marginBottom: 6}}>
                        <div style={{fontSize: 13, fontWeight: 600, color: "var(--fg-1)"}}>{it.label}</div>
                        <code style={{
                          fontSize: 10, padding: "1px 6px", color: "var(--fg-3)",
                          background: "var(--bg-2)", borderRadius: 3,
                        }}>{it.key}</code>
                        {it.is_secret && (
                          <span style={{
                            fontSize: 10, padding: "1px 6px", color: "#a855f7",
                            background: "color-mix(in oklch, #a855f7 12%, transparent)", borderRadius: 3,
                          }}>secret</span>
                        )}
                        {it.value_type !== "str" && (
                          <span style={{
                            fontSize: 10, padding: "1px 6px", color: "var(--fg-3)",
                            background: "var(--bg-2)", borderRadius: 3,
                          }}>{it.value_type}</span>
                        )}
                        {it.key === "CORS_ORIGINS" && (
                          <span style={{
                            fontSize: 10, padding: "1px 6px", color: "#ef4444",
                            background: "color-mix(in oklch, #ef4444 12%, transparent)", borderRadius: 3,
                          }}>需重启容器生效</span>
                        )}
                        <div style={{flex: 1}}/>
                        {it.updated_at && (
                          <span style={{fontSize: 10.5, color: "var(--fg-3)"}}>
                            上次更新 {_fmtTime(it.updated_at)}{it.updated_by ? ` · uid=${it.updated_by}` : ""}
                          </span>
                        )}
                      </div>

                      {it.description && (
                        <div style={{fontSize: 11, color: "var(--fg-3)", marginBottom: 8, lineHeight: 1.5}}>
                          {it.description}
                        </div>
                      )}

                      <div style={{display: "flex", gap: 8, alignItems: "stretch"}}>
                        <input
                          type={it.is_secret && !isRevealed ? "password" : "text"}
                          value={displayValue}
                          placeholder={it.has_value ? "" : "(空 → 回退到容器 env)"}
                          onChange={(e) => setDraft((s) => ({ ...s, [it.key]: e.target.value }))}
                          style={{
                            flex: 1, fontSize: 13, padding: "6px 10px",
                            background: "var(--bg-0)", color: "var(--fg-1)",
                            border: "1px solid var(--line-1)", borderRadius: 4,
                            fontFamily: it.is_secret ? "var(--font-mono)" : "var(--font-sans)",
                          }}
                          disabled={isSaving}
                        />
                        {it.is_secret && (
                          isRevealed
                            ? <button className="btn" onClick={() => onHide(it.key)} disabled={isSaving}>隐藏</button>
                            : <button className="btn" onClick={() => onReveal(it.key)} disabled={isSaving}>显示</button>
                        )}
                        <button
                          className="btn primary"
                          onClick={() => onSave(it)}
                          disabled={isSaving || !dirty}
                          title={dirty ? "保存改动" : "未修改"}
                        >
                          {isSaving ? "保存中…" : "保存"}
                        </button>
                        {flashType === "saved" && (
                          <span style={{fontSize: 11, color: "#22c55e", alignSelf: "center", marginLeft: 4}}>✓ 已保存</span>
                        )}
                        {flashType === "error" && (
                          <span style={{fontSize: 11, color: "#ef4444", alignSelf: "center", marginLeft: 4}}>✗ 失败</span>
                        )}
                      </div>
                    </div>
                  );
                })}
              </div>
            </section>
          );
        })}
      </div>
    </div>
  );
};

Object.assign(window, { AdminConfigsPage });
