import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { useNavigate, Navigate } from 'react-router-dom';
import toast from 'react-hot-toast';
import {
  FiShield, FiLogOut, FiMonitor, FiUsers, FiCpu, FiGrid, FiBox, FiTool, FiRefreshCw, FiSave,
  FiHome, FiRadio, FiLayers, FiActivity,
} from 'react-icons/fi';
import clsx from 'clsx';
import { PageWrapper } from '../styles/shared';
import { ENV_CONFIG } from '../config/env';
import { API_CATEGORIES, TOOLBOX_ITEMS, API_SOURCES } from '../config/Api60sConfig';
import { AI_MODEL_APPS } from '../config/StaticPageConfig';

const ADMIN_TOKEN = 'shumengya520';

const InfoCard = ({ children }) => (
  <div className="bg-white/95 rounded-[14px] p-6 shadow-[0_4px_12px_rgba(0,0,0,0.08)] mb-6">{children}</div>
);

const InfoRow = ({ label, value, valueStyle }) => (
  <div className="flex justify-between py-2.5 border-b border-gray-50 last:border-b-0">
    <span className="text-gray-500 text-sm">{label}</span>
    <span className="text-gray-800 font-semibold text-sm" style={valueStyle}>{value}</span>
  </div>
);

const ActionBtn = ({ children, onClick, disabled, type = 'button' }) => (
  <button
    type={type}
    onClick={onClick}
    disabled={disabled}
    className="flex items-center justify-center gap-2 px-5 py-3.5 border-none rounded-xl bg-gradient-to-br from-[#4ade80] to-[#22c55e] text-white text-sm font-semibold cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-[0_6px_16px_rgba(74,222,128,0.3)] disabled:opacity-50 disabled:cursor-not-allowed disabled:translate-y-0"
  >
    {children}
  </button>
);

const SixtyItem = ({ children, htmlFor }) => (
  <label
    htmlFor={htmlFor}
    className="flex items-center gap-2 text-sm text-gray-700 cursor-pointer select-none px-2 py-1.5 rounded-lg bg-gray-50 border border-gray-100 [&_input]:flex-shrink-0 [&_input]:[accent-color:#22c55e]"
  >
    {children}
  </label>
);

function normalizeHealthPayload(data) {
  if (!data || typeof data !== 'object') return null;
  const mysql = data.mysql;
  const mysqlOk = typeof mysql?.ok === 'boolean'
    ? mysql.ok
    : data.database === 'connected';
  const mysqlStatus = mysql?.status || data.database || (mysqlOk ? 'connected' : 'unknown');
  const sixty = data.sixty_api && typeof data.sixty_api === 'object' ? data.sixty_api : null;
  return {
    overall: data.status || 'unknown',
    ts: data.timestamp,
    backendApiOk: data.backend_api && typeof data.backend_api === 'object' ? data.backend_api.ok !== false : true,
    mysqlOk,
    mysqlStatus,
    sixty,
  };
}

const StatusPill = ({ ok, pending, unknown }) => {
  if (pending) return <span className="text-xs font-semibold text-gray-400">检测中…</span>;
  if (unknown) return <span className="text-xs font-semibold text-amber-800 bg-amber-100 px-2 py-0.5 rounded-md">未上报</span>;
  return (
    <span
      className={clsx(
        'text-xs font-bold px-2 py-0.5 rounded-md',
        ok ? 'bg-emerald-100 text-emerald-800' : 'bg-red-100 text-red-700',
      )}
    >
      {ok ? '正常' : '异常'}
    </span>
  );
};

const ADMIN_SECTIONS = [
  { id: 'overview', label: '概览', short: '概览', Icon: FiHome },
  { id: 'ai-upstream', label: 'AI 上游配置', short: 'AI 上游', Icon: FiCpu },
  { id: 'sixty-node', label: '60s 数据源', short: '60s 节点', Icon: FiRadio },
  { id: 'sixty-display', label: '60s 前台展示', short: '60s 展示', Icon: FiGrid },
  { id: 'ai-display', label: 'AI 前台展示', short: 'AI 展示', Icon: FiLayers },
];

const AdminPage = () => {
  const navigate = useNavigate();
  const [sysInfo, setSysInfo] = useState(null);
  const [loading, setLoading] = useState(false);
  const [disabled60s, setDisabled60s] = useState([]);
  const [loading60s, setLoading60s] = useState(true);
  const [saving60s, setSaving60s] = useState(false);
  const [aiBase, setAiBase] = useState('');
  const [aiKey, setAiKey] = useState('');
  const [aiModel, setAiModel] = useState('deepseek-chat');
  const [aiKeySet, setAiKeySet] = useState(false);
  const [aiKeyHint, setAiKeyHint] = useState('');
  const [loadingAi, setLoadingAi] = useState(false);
  const [savingAi, setSavingAi] = useState(false);
  const [sixtySrcId, setSixtySrcId] = useState('self');
  const [savingSixtySrc, setSavingSixtySrc] = useState(false);
  const [disabledAIModels, setDisabledAIModels] = useState([]);
  const [loadingAIModels, setLoadingAIModels] = useState(true);
  const [savingAIModels, setSavingAIModels] = useState(false);
  const [adminSection, setAdminSection] = useState(() => {
    try {
      const s = localStorage.getItem('infogenie_admin_section');
      if (s && ADMIN_SECTIONS.some((x) => x.id === s)) return s;
    } catch { /* ignore */ }
    return 'overview';
  });
  const [healthRaw, setHealthRaw] = useState(null);
  const [healthLoading, setHealthLoading] = useState(false);
  const [healthFetchError, setHealthFetchError] = useState(null);

  const disabled60sSet = useMemo(() => new Set(disabled60s), [disabled60s]);
  const disabledAIModelsSet = useMemo(() => new Set(disabledAIModels), [disabledAIModels]);

  const load60sConfig = useCallback(async () => {
    setLoading60s(true);
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/site/60s-disabled`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      setDisabled60s(Array.isArray(d.disabled) ? d.disabled.map(String) : []);
    } catch (e) {
      console.error(e);
      setDisabled60s([]);
      toast.error('无法加载 60s 展示配置，请确认后端已启动');
    }
    setLoading60s(false);
  }, []);

  const fetchSysInfo = useCallback(async () => {
    setLoading(true);
    try {
      const res = await fetch(`${ENV_CONFIG.API_URL}/`);
      const data = await res.json();
      setSysInfo(data);
    } catch (e) {
      console.error('获取系统信息失败:', e);
    }
    setLoading(false);
  }, []);

  const fetchAggregateHealth = useCallback(async () => {
    setHealthLoading(true);
    setHealthFetchError(null);
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/health`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      setHealthRaw(d);
    } catch (e) {
      console.error(e);
      setHealthRaw(null);
      setHealthFetchError(e.message || '请求失败');
    }
    setHealthLoading(false);
  }, []);

  const healthNorm = useMemo(() => normalizeHealthPayload(healthRaw), [healthRaw]);

  const loadAiRuntime = useCallback(async () => {
    if (localStorage.getItem('admin_token') !== ADMIN_TOKEN) return;
    setLoadingAi(true);
    try {
      const token = localStorage.getItem('admin_token') || '';
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/admin/site/ai-runtime`, {
        headers: { 'X-Site-Admin-Token': token },
      });
      if (!r.ok) throw new Error(String(r.status));
      const d = await r.json();
      setAiBase(d.api_base || '');
      setAiModel(d.default_model || 'deepseek-chat');
      setAiKeySet(!!d.api_key_set);
      setAiKeyHint(d.api_key_hint || '');
      setAiKey('');
    } catch (e) {
      console.error(e);
      toast.error('无法加载 AI 上游配置');
    }
    setLoadingAi(false);
  }, []);

  useEffect(() => { fetchSysInfo(); }, [fetchSysInfo]);
  useEffect(() => { fetchAggregateHealth(); }, [fetchAggregateHealth]);
  useEffect(() => { load60sConfig(); }, [load60sConfig]);
  useEffect(() => { loadAiRuntime(); }, [loadAiRuntime]);

  const load60sSource = useCallback(async () => {
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/site/60s-source`);
      if (!r.ok) throw new Error(String(r.status));
      const d = await r.json();
      setSixtySrcId(d.source_id || 'self');
    } catch (e) {
      console.error(e);
    }
  }, []);

  useEffect(() => { load60sSource(); }, [load60sSource]);

  const setItemVisible = (itemId, visible) => {
    setDisabled60s((prev) => {
      const s = new Set(prev);
      if (visible) s.delete(itemId);
      else s.add(itemId);
      return Array.from(s).sort();
    });
  };

  const save60sConfig = async () => {
    const token = localStorage.getItem('admin_token') || '';
    setSaving60s(true);
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/admin/site/60s-disabled`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', 'X-Site-Admin-Token': token },
        body: JSON.stringify({ disabled: disabled60s }),
      });
      let data = {};
      try { data = await r.json(); } catch { /* ignore */ }
      if (!r.ok) throw new Error(data.message || data.error || `HTTP ${r.status}`);
      toast.success(`已保存到后端（当前隐藏 ${disabled60s.length} 项）`);
    } catch (e) {
      toast.error(
        e.message?.includes('forbidden') || e.message === 'forbidden'
          ? '令牌无效：请确认后端 INFOGENIE_SITE_ADMIN_TOKEN 与管理员口令一致'
          : (e.message || '保存失败'),
      );
    }
    setSaving60s(false);
  };

  const saveAiRuntime = async () => {
    const token = localStorage.getItem('admin_token') || '';
    setSavingAi(true);
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/admin/site/ai-runtime`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', 'X-Site-Admin-Token': token },
        body: JSON.stringify({
          api_base: aiBase.trim(),
          api_key: aiKey.trim(),
          default_model: aiModel.trim() || 'deepseek-chat',
          default_provider: 'deepseek',
        }),
      });
      const data = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(data.message || data.error || `HTTP ${r.status}`);
      toast.success('AI 上游配置已保存');
      await loadAiRuntime();
    } catch (e) {
      toast.error(e.message || '保存失败');
    }
    setSavingAi(false);
  };

  const save60sSource = async () => {
    const token = localStorage.getItem('admin_token') || '';
    setSavingSixtySrc(true);
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/admin/site/60s-source`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', 'X-Site-Admin-Token': token },
        body: JSON.stringify({ source_id: sixtySrcId }),
      });
      const data = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(data.message || data.error || `HTTP ${r.status}`);
      toast.success(`60s 节点已保存：${data.label || sixtySrcId}`);
      await load60sSource();
    } catch (e) {
      toast.error(e.message || '保存失败');
    }
    setSavingSixtySrc(false);
  };

  const loadAIModelConfig = useCallback(async () => {
    setLoadingAIModels(true);
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/site/ai-model-disabled`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      setDisabledAIModels(Array.isArray(d.disabled) ? d.disabled.map(String) : []);
    } catch (e) {
      console.error(e);
      setDisabledAIModels([]);
      toast.error('无法加载 AI 应用配置，请确认后端已启动');
    }
    setLoadingAIModels(false);
  }, []);

  useEffect(() => { loadAIModelConfig(); }, [loadAIModelConfig]);

  const saveAIModelConfig = async () => {
    const token = localStorage.getItem('admin_token') || '';
    setSavingAIModels(true);
    try {
      const r = await fetch(`${ENV_CONFIG.API_URL}/api/admin/site/ai-model-disabled`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', 'X-Site-Admin-Token': token },
        body: JSON.stringify({ disabled: disabledAIModels }),
      });
      const data = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(data.message || data.error || `HTTP ${r.status}`);
      toast.success(`AI 应用配置已保存，隐藏 ${disabledAIModels.length} 个应用`);
    } catch (e) {
      toast.error(e.message || '保存失败');
    }
    setSavingAIModels(false);
  };

  const setAIModelVisible = (appId, visible) => {
    if (visible) setDisabledAIModels((prev) => prev.filter((id) => id !== appId));
    else setDisabledAIModels((prev) => [...prev, appId]);
  };

  const storedToken = localStorage.getItem('admin_token');
  if (storedToken !== ADMIN_TOKEN) return <Navigate to="/" replace />;

  const handleLogout = () => {
    localStorage.removeItem('admin_token');
    navigate('/', { replace: true });
  };

  const handleClearBuildCache = () => {
    if ('caches' in window) {
      caches.keys().then((names) => { names.forEach((name) => caches.delete(name)); });
    }
    alert('缓存已清除，请刷新页面');
  };

  const handleClearLocalStorage = () => {
    const savedAdmin = localStorage.getItem('admin_token');
    const savedUser = localStorage.getItem('token');
    localStorage.clear();
    if (savedAdmin) localStorage.setItem('admin_token', savedAdmin);
    if (savedUser) localStorage.setItem('token', savedUser);
    alert('本地存储已清理（保留登录状态）');
  };

  const gameCount = 8;
  const apiCount = API_CATEGORIES.reduce((sum, c) => sum + c.items.length, 0);
  const toolboxCount = TOOLBOX_ITEMS.length;
  const aiCount = AI_MODEL_APPS.filter((a) => a.IsShow !== false).length;

  const inputCls = 'w-full px-3 py-2.5 rounded-lg border border-gray-200 text-sm box-border font-[inherit] focus:outline-none focus:border-[#4ade80]';

  const selectSection = (id) => {
    setAdminSection(id);
    try {
      localStorage.setItem('infogenie_admin_section', id);
    } catch { /* ignore */ }
  };

  const activeMeta = ADMIN_SECTIONS.find((s) => s.id === adminSection) || ADMIN_SECTIONS[0];

  return (
    <PageWrapper>
      <div className="max-w-[1400px] mx-auto px-4 pb-10 w-full">
        <div className="flex flex-wrap items-center justify-between gap-3 py-5 border-b-2 border-white/20 mb-5">
          <h1 className="flex items-center gap-3 text-2xl text-white [text-shadow:0_2px_8px_rgba(0,0,0,0.2)] m-0">
            <FiShield /> 管理员后台
          </h1>
          <button
            type="button"
            onClick={handleLogout}
            className="flex items-center gap-1.5 px-5 py-2.5 border-none rounded-[10px] bg-red-500/80 text-white text-sm font-semibold cursor-pointer transition-colors hover:bg-red-500"
          >
            <FiLogOut size={16} /> 退出后台
          </button>
        </div>

        <div className="flex flex-col md:flex-row gap-4 md:gap-6 md:items-start">
          <aside
            className={clsx(
              'flex-shrink-0 md:w-56',
              'flex flex-row md:flex-col gap-1.5',
              'overflow-x-auto md:overflow-visible pb-1 md:pb-0',
              '-mx-1 px-1 md:mx-0 md:px-0',
              '[scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
              'md:sticky md:top-[72px] md:self-start',
            )}
          >
            <p className="hidden md:block text-xs font-semibold text-white/70 uppercase tracking-wide mb-2 px-2">
              功能分区
            </p>
            {ADMIN_SECTIONS.map(({ id, short, Icon }) => (
              <button
                key={id}
                type="button"
                onClick={() => selectSection(id)}
                className={clsx(
                  'flex items-center gap-2.5 w-full min-w-max md:min-w-0 text-left',
                  'px-3 py-2.5 rounded-xl text-sm font-medium transition-all border',
                  'whitespace-nowrap md:whitespace-normal',
                  adminSection === id
                    ? 'bg-white/95 text-[#166534] border-white shadow-md'
                    : 'bg-white/15 text-white border-white/25 hover:bg-white/25',
                )}
              >
                <Icon size={18} className="flex-shrink-0 opacity-90" />
                <span>{short}</span>
              </button>
            ))}
          </aside>

          <main className="flex-1 min-w-0 space-y-1 max-h-[calc(100vh-220px)] md:max-h-[calc(100vh-180px)] overflow-y-auto pr-1 [scrollbar-width:thin]">
            <div className="mb-3">
              <h2 className="text-lg text-white [text-shadow:0_1px_4px_rgba(0,0,0,0.15)] m-0">
                {activeMeta.label}
              </h2>
              <p className="text-sm text-white/80 m-0 mt-1">
                在左侧切换分区；窄屏下在顶部横向滑动选择。
              </p>
            </div>

            {adminSection === 'overview' && (
              <>
                <div className="grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3 mb-6">
                  {[
                    { icon: FiGrid, bg: 'linear-gradient(135deg,#4ade80,#22c55e)', label: '60sAPI', value: apiCount },
                    { icon: FiBox, bg: 'linear-gradient(135deg,#60a5fa,#3b82f6)', label: '休闲游戏', value: gameCount },
                    { icon: FiTool, bg: 'linear-gradient(135deg,#a78bfa,#8b5cf6)', label: '工具箱', value: toolboxCount },
                    { icon: FiCpu, bg: 'linear-gradient(135deg,#f472b6,#ec4899)', label: 'AI 应用', value: aiCount },
                    { icon: FiUsers, bg: 'linear-gradient(135deg,#fbbf24,#f59e0b)', label: '认证中心', value: '已接入' },
                  ].map(({ icon: Icon, bg, label, value }) => (
                    <div key={label} className="bg-white/95 rounded-[14px] p-5 shadow-[0_4px_12px_rgba(0,0,0,0.08)] flex items-center gap-4">
                      <div className="w-12 h-12 rounded-xl flex items-center justify-center text-[22px] text-white flex-shrink-0" style={{ background: bg }}>
                        <Icon />
                      </div>
                      <div>
                        <div className="text-sm text-gray-500">{label}</div>
                        <div className="text-[22px] font-bold text-gray-800">{value}</div>
                      </div>
                    </div>
                  ))}
                </div>

                <h3 className="text-base text-white mb-3 [text-shadow:0_1px_4px_rgba(0,0,0,0.12)] flex items-center gap-2">
                  <FiActivity className="opacity-90" />
                  状态监控
                </h3>
                <InfoCard>
                  <div className="flex flex-wrap items-start justify-between gap-3 mb-4">
                    <p className="text-sm text-gray-500 m-0 leading-snug max-w-xl">
                      数据源：<code className="text-xs bg-gray-100 px-1 rounded">GET /api/health</code>
                      。含 MySQL Ping 与当前配置的 60s 上游探测（请求其公开接口 <code className="text-xs bg-gray-100 px-1 rounded">/v2/ip</code>）。
                      {healthNorm?.overall === 'degraded' && (
                        <span className="text-amber-700 font-semibold"> 当前整体为 degraded。</span>
                      )}
                    </p>
                    <button
                      type="button"
                      onClick={fetchAggregateHealth}
                      disabled={healthLoading}
                      className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold border border-gray-200 bg-white text-gray-800 hover:bg-gray-50 disabled:opacity-50 shrink-0"
                    >
                      <FiRefreshCw size={16} className={healthLoading ? 'animate-spin' : ''} />
                      {healthLoading ? '检测中…' : '重新检测'}
                    </button>
                  </div>
                  {healthFetchError && (
                    <p className="text-sm text-red-600 mb-3 m-0">
                      无法连接健康检查接口：{healthFetchError}（请确认 Go 后端已启动且已更新到包含聚合健康字段的版本）
                    </p>
                  )}
                  <div className="space-y-3">
                    <div className="flex flex-wrap items-center justify-between gap-2 py-2 border-b border-gray-100">
                      <div>
                        <div className="text-gray-800 font-semibold text-sm">万象后端 API（InfoGenie Go）</div>
                        <div className="text-gray-400 text-xs mt-0.5">{ENV_CONFIG.API_URL}</div>
                      </div>
                      <StatusPill
                        ok={!healthFetchError && (healthNorm?.backendApiOk !== false)}
                        pending={healthLoading && !healthRaw && !healthFetchError}
                        unknown={false}
                      />
                    </div>
                    <div className="flex flex-wrap items-center justify-between gap-2 py-2 border-b border-gray-100">
                      <div>
                        <div className="text-gray-800 font-semibold text-sm">MySQL 数据库</div>
                        <div className="text-gray-500 text-xs mt-0.5">
                          {healthNorm ? `状态字段：${healthNorm.mysqlStatus}` : '—'}
                        </div>
                      </div>
                      <StatusPill
                        ok={!!healthNorm?.mysqlOk}
                        pending={healthLoading && !healthRaw && !healthFetchError}
                        unknown={!healthFetchError && !healthLoading && !healthRaw}
                      />
                    </div>
                    <div className="flex flex-wrap items-start justify-between gap-2 py-2">
                      <div className="min-w-0 flex-1">
                        <div className="text-gray-800 font-semibold text-sm">60s API 上游（当前节点）</div>
                        {healthNorm?.sixty ? (
                          <div className="text-gray-500 text-xs mt-1 space-y-0.5 break-all">
                            <div>
                              {healthNorm.sixty.label || healthNorm.sixty.source_id}
                              {healthNorm.sixty.base_url ? ` · ${healthNorm.sixty.base_url}` : ''}
                            </div>
                            {typeof healthNorm.sixty.latency_ms === 'number' && (
                              <div>探测耗时 {healthNorm.sixty.latency_ms} ms</div>
                            )}
                            {healthNorm.sixty.probe_url && (
                              <div className="text-gray-400">探测 URL：{healthNorm.sixty.probe_url}</div>
                            )}
                            {healthNorm.sixty.error ? (
                              <div className="text-red-600 font-medium">错误：{String(healthNorm.sixty.error)}</div>
                            ) : null}
                            {typeof healthNorm.sixty.http_status === 'number' && healthNorm.sixty.http_status > 0 ? (
                              <div>HTTP {healthNorm.sixty.http_status}</div>
                            ) : null}
                          </div>
                        ) : (
                          <div className="text-gray-500 text-xs mt-1">
                            {!healthFetchError && healthRaw ? '响应中无 sixty_api 字段，请升级后端。' : '—'}
                          </div>
                        )}
                      </div>
                      <StatusPill
                        ok={!!healthNorm?.sixty?.ok}
                        pending={healthLoading && !healthRaw && !healthFetchError}
                        unknown={!healthFetchError && !!healthRaw && !healthNorm?.sixty}
                      />
                    </div>
                  </div>
                </InfoCard>

                <h3 className="text-base text-white mb-3 [text-shadow:0_1px_4px_rgba(0,0,0,0.12)]">系统信息</h3>
                <InfoCard>
                  <InfoRow label="前端环境" value={ENV_CONFIG.ENV || 'development'} />
                  <InfoRow label="API 地址" value={ENV_CONFIG.API_URL} />
                  <InfoRow label="认证中心" value={ENV_CONFIG.AUTH_URL || '未配置'} />
                  <InfoRow label="认证中心 API" value={ENV_CONFIG.AUTH_API_URL || '未配置'} />
                  <InfoRow label="后端状态" value={loading ? '检测中...' : sysInfo ? '在线' : '离线'} valueStyle={{ color: sysInfo ? '#22c55e' : '#ef4444' }} />
                  {sysInfo && (
                    <>
                      <InfoRow label="后端版本" value={sysInfo.version || sysInfo.name || '-'} />
                      <InfoRow label="后端描述" value={sysInfo.description || sysInfo.message || '-'} />
                    </>
                  )}
                  <InfoRow label="浏览器" value={navigator.userAgent.split(' ').slice(-2).join(' ')} />
                  <InfoRow label="屏幕分辨率" value={`${window.screen.width} × ${window.screen.height}`} />
                </InfoCard>

                <h3 className="text-base text-white mb-3 mt-2 [text-shadow:0_1px_4px_rgba(0,0,0,0.12)]">快捷操作</h3>
                <div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3 mb-2">
                  <ActionBtn onClick={fetchSysInfo} disabled={loading}><FiRefreshCw size={16} /> 刷新后端状态</ActionBtn>
                  <ActionBtn onClick={handleClearBuildCache}><FiMonitor size={16} /> 清除 SW 缓存</ActionBtn>
                  <ActionBtn onClick={handleClearLocalStorage}><FiBox size={16} /> 清理本地存储</ActionBtn>
                  <ActionBtn onClick={() => window.open(ENV_CONFIG.AUTH_URL, '_blank')}><FiUsers size={16} /> 打开认证中心</ActionBtn>
                </div>
              </>
            )}

            {adminSection === 'ai-upstream' && (
              <InfoCard>
          <p className="text-sm text-gray-500 leading-[1.55] mb-3">
            此处配置会写入数据库表 <code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">site_ai_runtime</code>，并<strong>优先于</strong>服务端 <code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">ai_config.json</code>。
            密钥留空表示<strong>不修改</strong>已保存的 API Key。
          </p>
          {loadingAi ? (
            <p className="text-sm text-gray-500">正在加载…</p>
          ) : (
            <>
              {[
                { id: 'ai-base', label: 'API Base URL', type: 'text', value: aiBase, onChange: setAiBase, placeholder: 'https://api.deepseek.com' },
                { id: 'ai-key', label: `API Key${aiKeySet ? `（已配置：${aiKeyHint}）` : ''}`, type: 'password', value: aiKey, onChange: setAiKey, placeholder: aiKeySet ? '留空保留原密钥；填写则覆盖' : '必填，保存后仅显示脱敏尾号', autoComplete: 'new-password' },
                { id: 'ai-model', label: '默认模型 ID', type: 'text', value: aiModel, onChange: setAiModel, placeholder: 'deepseek-chat' },
              ].map(({ id, label, type, value, onChange, placeholder, autoComplete }) => (
                <div key={id} className="mb-3.5 max-w-[560px]">
                  <label htmlFor={id} className="block text-xs font-semibold text-gray-700 mb-1.5">{label}</label>
                  <input id={id} type={type} value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} autoComplete={autoComplete} className={inputCls} />
                </div>
              ))}
              <div className="flex gap-3 mt-5 flex-wrap items-center">
                <ActionBtn onClick={saveAiRuntime} disabled={savingAi}><FiSave size={16} /> {savingAi ? '保存中…' : '保存 AI 配置'}</ActionBtn>
                <ActionBtn onClick={loadAiRuntime} disabled={loadingAi}><FiRefreshCw size={16} /> 重新加载</ActionBtn>
              </div>
            </>
          )}
              </InfoCard>
            )}

            {adminSection === 'sixty-node' && (
              <InfoCard>
                <p className="text-sm text-gray-500 leading-[1.55] mb-3">
                  选择 60s 类接口的<strong>上游根地址</strong>（与 <code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">Api60sConfig.API_SOURCES</code> 对齐）。
                </p>
                <div className="grid grid-cols-[repeat(auto-fill,minmax(210px,1fr))] gap-[8px_14px] max-w-[520px]">
                  {API_SOURCES.map((s) => (
                    <SixtyItem key={s.id} htmlFor={`admin-sixty-${s.id}`}>
                      <input type="radio" id={`admin-sixty-${s.id}`} name="admin_sixty_src" checked={sixtySrcId === s.id} onChange={() => setSixtySrcId(s.id)} />
                      <span>{s.label} <span className="text-gray-400 text-[11px] font-medium">({s.baseUrl.replace(/^https?:\/\//, '')})</span></span>
                    </SixtyItem>
                  ))}
                </div>
                <div className="flex gap-3 mt-5 flex-wrap items-center">
                  <ActionBtn onClick={save60sSource} disabled={savingSixtySrc}><FiSave size={16} /> {savingSixtySrc ? '保存中…' : '保存节点'}</ActionBtn>
                  <ActionBtn onClick={load60sSource}><FiRefreshCw size={16} /> 重新加载</ActionBtn>
                </div>
              </InfoCard>
            )}

            {adminSection === 'sixty-display' && (
              <InfoCard>
                <p className="text-sm text-gray-500 leading-[1.55] mb-3">
                  勾选表示<strong>在前台展示</strong>该功能；取消勾选将写入后端表 <code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">site_60s_disabled</code>。
                </p>
                {loading60s ? (
                  <p className="text-sm text-gray-500">正在加载配置…</p>
                ) : (
                  <>
                    {API_CATEGORIES.map((cat) => (
                      <div key={cat.id}>
                        <h3 className="text-[15px] text-[#166534] mt-[18px] mb-2.5 first:mt-1">{cat.icon} {cat.title}</h3>
                        <div className="grid grid-cols-[repeat(auto-fill,minmax(210px,1fr))] gap-[8px_14px]">
                          {cat.items.map((item) => (
                            <SixtyItem key={item.id}>
                              <input type="checkbox" checked={!disabled60sSet.has(item.id)} onChange={(e) => setItemVisible(item.id, e.target.checked)} />
                              <span>{item.icon} {item.title}</span>
                            </SixtyItem>
                          ))}
                        </div>
                      </div>
                    ))}
                    <div className="flex gap-3 mt-5 flex-wrap items-center">
                      <ActionBtn onClick={save60sConfig} disabled={saving60s}><FiSave size={16} /> {saving60s ? '保存中…' : '保存到后端'}</ActionBtn>
                      <ActionBtn onClick={load60sConfig} disabled={loading60s}><FiRefreshCw size={16} /> 重新加载</ActionBtn>
                    </div>
                  </>
                )}
              </InfoCard>
            )}

            {adminSection === 'ai-display' && (
              <InfoCard>
                <p className="text-sm text-gray-500 leading-[1.55] mb-3">
                  勾选表示<strong>在前台展示</strong>该AI应用；取消勾选将写入后端表 <code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">site_ai_model_disabled</code>。
                </p>
                {loadingAIModels ? (
                  <p className="text-sm text-gray-500">正在加载配置…</p>
                ) : (
                  <>
                    <div className="grid grid-cols-[repeat(auto-fill,minmax(210px,1fr))] gap-[8px_14px]">
                      {AI_MODEL_APPS.map((app) => (
                        <SixtyItem key={app.id}>
                          <input type="checkbox" checked={!disabledAIModelsSet.has(app.id)} onChange={(e) => setAIModelVisible(app.id, e.target.checked)} />
                          <span>{app.icon} {app.title}</span>
                        </SixtyItem>
                      ))}
                    </div>
                    <div className="flex gap-3 mt-5 flex-wrap items-center">
                      <ActionBtn onClick={saveAIModelConfig} disabled={savingAIModels}><FiSave size={16} /> {savingAIModels ? '保存中…' : '保存到后端'}</ActionBtn>
                      <ActionBtn onClick={loadAIModelConfig} disabled={loadingAIModels}><FiRefreshCw size={16} /> 重新加载</ActionBtn>
                    </div>
                  </>
                )}
              </InfoCard>
            )}
          </main>
        </div>
      </div>
    </PageWrapper>
  );
};

export default AdminPage;
