import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { useNavigate, Navigate } from 'react-router-dom';
import styled from 'styled-components';
import toast from 'react-hot-toast';
import { FiShield, FiLogOut, FiMonitor, FiUsers, FiCpu, FiGrid, FiBox, FiTool, FiRefreshCw, FiSave } from 'react-icons/fi';
import { PageWrapper, Container } 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';
import { fetchAIModelDisabledSet, filterAIModelApps } from '../utils/aiModelVisibility';

const ADMIN_TOKEN = 'shumengya520';

const AdminHeader = styled.div`
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 0;
  border-bottom: 2px solid rgba(255,255,255,0.2);
  margin-bottom: 30px;
`;

const AdminTitle = styled.h1`
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 24px;
  color: #fff;
  text-shadow: 0 2px 8px rgba(0,0,0,0.2);
`;

const LogoutBtn = styled.button`
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 20px;
  border: none;
  border-radius: 10px;
  background: rgba(239,68,68,0.8);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.2s;
  &:hover { background: rgba(239,68,68,1); }
`;

const StatsGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-bottom: 30px;
`;

const StatCard = styled.div`
  background: rgba(255,255,255,0.95);
  border-radius: 14px;
  padding: 20px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
  display: flex;
  align-items: center;
  gap: 16px;
`;

const StatIcon = styled.div`
  width: 48px;
  height: 48px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: #fff;
  background: ${({ $bg }) => $bg || 'linear-gradient(135deg, #4ade80, #22c55e)'};
  flex-shrink: 0;
`;

const StatInfo = styled.div``;
const StatLabel = styled.div`font-size: 13px; color: #6b7280;`;
const StatValue = styled.div`font-size: 22px; font-weight: 700; color: #1f2937;`;

const SectionTitle = styled.h2`
  font-size: 18px;
  color: #fff;
  margin-bottom: 16px;
  text-shadow: 0 1px 4px rgba(0,0,0,0.15);
`;

const InfoCard = styled.div`
  background: rgba(255,255,255,0.95);
  border-radius: 14px;
  padding: 24px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
  margin-bottom: 24px;
`;

const InfoRow = styled.div`
  display: flex;
  justify-content: space-between;
  padding: 10px 0;
  border-bottom: 1px solid #f3f4f6;
  &:last-child { border-bottom: none; }
`;

const InfoLabel = styled.span`color: #6b7280; font-size: 14px;`;
const InfoValue = styled.span`color: #1f2937; font-weight: 600; font-size: 14px;`;

const ActionGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
  margin-bottom: 24px;
`;

const ActionBtn = styled.button`
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 20px;
  border: none;
  border-radius: 12px;
  background: linear-gradient(135deg, #4ade80, #22c55e);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s;
  &:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(74,222,128,0.3); }
  &:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
`;

const SixtyHint = styled.p`
  font-size: 13px;
  color: #6b7280;
  line-height: 1.55;
  margin: 0 0 12px;
`;

const SixtyCatTitle = styled.h3`
  font-size: 15px;
  color: #166534;
  margin: 18px 0 10px;
  &:first-of-type { margin-top: 4px; }
`;

const SixtyGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 8px 14px;
`;

const SixtyItem = styled.label`
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: #374151;
  cursor: pointer;
  user-select: none;
  padding: 6px 8px;
  border-radius: 8px;
  background: #f9fafb;
  border: 1px solid #f3f4f6;
  input { flex-shrink: 0; accent-color: #22c55e; }
`;

const SixtyActions = styled.div`
  display: flex;
  gap: 12px;
  margin-top: 20px;
  flex-wrap: wrap;
  align-items: center;
`;

const AiField = styled.div`
  margin-bottom: 14px;
  max-width: 560px;
`;

const AiLabel = styled.label`
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: #374151;
  margin-bottom: 6px;
`;

const AiInput = styled.input`
  width: 100%;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid #e5e7eb;
  font-size: 14px;
  box-sizing: border-box;
  font-family: inherit;
`;

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 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 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(() => { 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]);
    }
  };

  // 所有 Hook 调用完毕后再做条件判断
  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;

  return (
    <PageWrapper>
      <Container>
        <AdminHeader>
          <AdminTitle><FiShield /> 管理员后台</AdminTitle>
          <LogoutBtn onClick={handleLogout}><FiLogOut size={16} /> 退出后台</LogoutBtn>
        </AdminHeader>

        <StatsGrid>
          <StatCard>
            <StatIcon $bg="linear-gradient(135deg, #4ade80, #22c55e)"><FiGrid /></StatIcon>
            <StatInfo>
              <StatLabel>60sAPI</StatLabel>
              <StatValue>{apiCount}</StatValue>
            </StatInfo>
          </StatCard>
          <StatCard>
            <StatIcon $bg="linear-gradient(135deg, #60a5fa, #3b82f6)"><FiBox /></StatIcon>
            <StatInfo>
              <StatLabel>休闲游戏</StatLabel>
              <StatValue>{gameCount}</StatValue>
            </StatInfo>
          </StatCard>
          <StatCard>
            <StatIcon $bg="linear-gradient(135deg, #a78bfa, #8b5cf6)"><FiTool /></StatIcon>
            <StatInfo>
              <StatLabel>工具箱</StatLabel>
              <StatValue>{toolboxCount}</StatValue>
            </StatInfo>
          </StatCard>
          <StatCard>
            <StatIcon $bg="linear-gradient(135deg, #f472b6, #ec4899)"><FiCpu /></StatIcon>
            <StatInfo>
              <StatLabel>AI 应用</StatLabel>
              <StatValue>{aiCount}</StatValue>
            </StatInfo>
          </StatCard>
          <StatCard>
            <StatIcon $bg="linear-gradient(135deg, #fbbf24, #f59e0b)"><FiUsers /></StatIcon>
            <StatInfo>
              <StatLabel>认证中心</StatLabel>
              <StatValue>已接入</StatValue>
            </StatInfo>
          </StatCard>
        </StatsGrid>

        <SectionTitle>系统信息</SectionTitle>
        <InfoCard>
          <InfoRow>
            <InfoLabel>前端环境</InfoLabel>
            <InfoValue>{ENV_CONFIG.ENV || 'development'}</InfoValue>
          </InfoRow>
          <InfoRow>
            <InfoLabel>API 地址</InfoLabel>
            <InfoValue>{ENV_CONFIG.API_URL}</InfoValue>
          </InfoRow>
          <InfoRow>
            <InfoLabel>认证中心</InfoLabel>
            <InfoValue>{ENV_CONFIG.AUTH_URL || '未配置'}</InfoValue>
          </InfoRow>
          <InfoRow>
            <InfoLabel>认证中心 API</InfoLabel>
            <InfoValue>{ENV_CONFIG.AUTH_API_URL || '未配置'}</InfoValue>
          </InfoRow>
          <InfoRow>
            <InfoLabel>后端状态</InfoLabel>
            <InfoValue style={{ color: sysInfo ? '#22c55e' : '#ef4444' }}>
              {loading ? '检测中...' : sysInfo ? '在线' : '离线'}
            </InfoValue>
          </InfoRow>
          {sysInfo && (
            <>
              <InfoRow>
                <InfoLabel>后端版本</InfoLabel>
                <InfoValue>{sysInfo.version || sysInfo.name || '-'}</InfoValue>
              </InfoRow>
              <InfoRow>
                <InfoLabel>后端描述</InfoLabel>
                <InfoValue>{sysInfo.description || sysInfo.message || '-'}</InfoValue>
              </InfoRow>
            </>
          )}
          <InfoRow>
            <InfoLabel>浏览器</InfoLabel>
            <InfoValue>{navigator.userAgent.split(' ').slice(-2).join(' ')}</InfoValue>
          </InfoRow>
          <InfoRow>
            <InfoLabel>屏幕分辨率</InfoLabel>
            <InfoValue>{window.screen.width} × {window.screen.height}</InfoValue>
          </InfoRow>
        </InfoCard>

        <SectionTitle>快捷操作</SectionTitle>
        <ActionGrid>
          <ActionBtn onClick={fetchSysInfo} disabled={loading}>
            <FiRefreshCw size={16} /> 刷新后端状态
          </ActionBtn>
          <ActionBtn onClick={handleClearBuildCache}>
            <FiMonitor size={16} /> 清除 Service Worker 缓存
          </ActionBtn>
          <ActionBtn onClick={handleClearLocalStorage}>
            <FiBox size={16} /> 清理本地存储
          </ActionBtn>
          <ActionBtn onClick={() => window.open(ENV_CONFIG.AUTH_URL, '_blank')}>
            <FiUsers size={16} /> 打开认证中心
          </ActionBtn>
        </ActionGrid>

        <SectionTitle>AI 应用上游（DeepSeek 兼容 OpenAI Chat）</SectionTitle>
        <InfoCard>
          <SixtyHint>
            此处配置会写入数据库表 <code>site_ai_runtime</code>，并<strong>优先于</strong>服务端 <code>ai_config.json</code>。
            API Base 示例：<code>https://api.deepseek.com</code>（后端会自动拼接 <code>/chat/completions</code>）。
            各 AI 应用的<strong>提示词</strong>已放在前端 <code>public/aimodelapp/shared/ai-prompts.js</code>，统一经 <code>/api/aimodelapp/chat</code> 调用。
            密钥留空表示<strong>不修改</strong>已保存的 API Key。
          </SixtyHint>
          {loadingAi ? (
            <SixtyHint>正在加载…</SixtyHint>
          ) : (
            <>
              <AiField>
                <AiLabel htmlFor="ai-base">API Base URL</AiLabel>
                <AiInput
                  id="ai-base"
                  value={aiBase}
                  onChange={(e) => setAiBase(e.target.value)}
                  placeholder="https://api.deepseek.com"
                  autoComplete="off"
                />
              </AiField>
              <AiField>
                <AiLabel htmlFor="ai-key">API Key{aiKeySet ? `（已配置：${aiKeyHint}）` : ''}</AiLabel>
                <AiInput
                  id="ai-key"
                  type="password"
                  value={aiKey}
                  onChange={(e) => setAiKey(e.target.value)}
                  placeholder={aiKeySet ? '留空保留原密钥；填写则覆盖' : '必填，保存后仅显示脱敏尾号'}
                  autoComplete="new-password"
                />
              </AiField>
              <AiField>
                <AiLabel htmlFor="ai-model">默认模型 ID</AiLabel>
                <AiInput
                  id="ai-model"
                  value={aiModel}
                  onChange={(e) => setAiModel(e.target.value)}
                  placeholder="deepseek-chat"
                  autoComplete="off"
                />
              </AiField>
              <SixtyActions>
                <ActionBtn type="button" onClick={saveAiRuntime} disabled={savingAi}>
                  <FiSave size={16} /> {savingAi ? '保存中…' : '保存 AI 配置'}
                </ActionBtn>
                <ActionBtn type="button" onClick={loadAiRuntime} disabled={loadingAi}>
                  <FiRefreshCw size={16} /> 重新加载
                </ActionBtn>
              </SixtyActions>
            </>
          )}
        </InfoCard>

        <SectionTitle>60s 数据源节点</SectionTitle>
        <InfoCard>
          <SixtyHint>
            选择 60s 类接口的<strong>上游根地址</strong>（与 <code>Api60sConfig.API_SOURCES</code> 对齐）。前台列表仅展示当前节点，用户不可切换；静态页通过 iframe 参数注入 base。
          </SixtyHint>
          <SixtyGrid style={{ maxWidth: 520 }}>
            {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 style={{ color: '#9ca3af', fontSize: 11, fontWeight: 500 }}>
                    ({s.baseUrl.replace(/^https?:\/\//, '')})
                  </span>
                </span>
              </SixtyItem>
            ))}
          </SixtyGrid>
          <SixtyActions>
            <ActionBtn type="button" onClick={save60sSource} disabled={savingSixtySrc}>
              <FiSave size={16} /> {savingSixtySrc ? '保存中…' : '保存节点'}
            </ActionBtn>
            <ActionBtn type="button" onClick={load60sSource}>
              <FiRefreshCw size={16} /> 重新加载
            </ActionBtn>
          </SixtyActions>
        </InfoCard>

        <SectionTitle>60s API 前台展示</SectionTitle>
        <InfoCard>
          <SixtyHint>
            勾选表示<strong>在前台展示</strong>该功能；取消勾选将写入后端表 <code>site_60s_disabled</code>，用户无法在列表与详情中打开（直接 URL 也会被拦）。
            保存时需在后端配置环境变量 <code>INFOGENIE_SITE_ADMIN_TOKEN</code>，取值与当前管理员口令（连点 Logo 解锁）<strong>一致</strong>。
          </SixtyHint>
          {loading60s ? (
            <SixtyHint>正在加载配置…</SixtyHint>
          ) : (
            <>
              {API_CATEGORIES.map((cat) => (
                <div key={cat.id}>
                  <SixtyCatTitle>{cat.icon} {cat.title}</SixtyCatTitle>
                  <SixtyGrid>
                    {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>
                    ))}
                  </SixtyGrid>
                </div>
              ))}
              <SixtyActions>
                <ActionBtn type="button" onClick={save60sConfig} disabled={saving60s}>
                  <FiSave size={16} /> {saving60s ? '保存中…' : '保存到后端'}
                </ActionBtn>
                <ActionBtn type="button" onClick={load60sConfig} disabled={loading60s}>
                  <FiRefreshCw size={16} /> 重新加载
                </ActionBtn>
              </SixtyActions>
            </>
          )}
        </InfoCard>

        <SectionTitle>AI 应用前台展示</SectionTitle>
        <InfoCard>
          <SixtyHint>
            勾选表示<strong>在前台展示</strong>该AI应用；取消勾选将写入后端表 <code>site_ai_model_disabled</code>，用户无法在列表中看到或打开该应用。
            保存时需在后端配置环境变量 <code>INFOGENIE_SITE_ADMIN_TOKEN</code>，取值与当前管理员口令（连点 Logo 解锁）<strong>一致</strong>。
          </SixtyHint>
          {loadingAIModels ? (
            <SixtyHint>正在加载配置…</SixtyHint>
          ) : (
            <>
              <SixtyGrid>
                {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>
                ))}
              </SixtyGrid>
              <SixtyActions>
                <ActionBtn type="button" onClick={saveAIModelConfig} disabled={savingAIModels}>
                  <FiSave size={16} /> {savingAIModels ? '保存中…' : '保存到后端'}
                </ActionBtn>
                <ActionBtn type="button" onClick={loadAIModelConfig} disabled={loadingAIModels}>
                  <FiRefreshCw size={16} /> 重新加载
                </ActionBtn>
              </SixtyActions>
            </>
          )}
        </InfoCard>
      </Container>
    </PageWrapper>
  );
};

export default AdminPage;
