/**
 * SettingsModal — 设置弹窗
 */

import { useState, useEffect, useCallback } from 'react';
import { Dialog, DialogContent, DialogTitle, Button, TextField, Select, MenuItem, Tabs, Tab, Checkbox, Box, Typography, Stack, Divider, FormControlLabel, InputLabel, FormControl, IconButton, Chip, Switch, Alert, CircularProgress } from '@mui/material';
import { alpha } from '@mui/material/styles';
import { X, Settings, Bot, Wrench, Server, Palette, FileText, Eye, EyeOff, FolderOpen, Globe } from 'lucide-react';
import { useUIStore, type ThemeMode } from '@renderer/stores/ui-store';
import { useAgentStore } from '@renderer/stores/agent-store';
import { PROVIDER_LABELS } from '@renderer/lib/constants';

type SettingsTab = 'workspace' | 'llm' | 'agent' | 'tools' | 'mcp' | 'searxng' | 'appearance' | 'logs';
const TABS: { id: SettingsTab; label: string; icon: typeof Settings }[] = [
  { id: 'workspace', label: '工作空间', icon: FolderOpen },
  { id: 'llm', label: 'LLM 配置', icon: Bot }, { id: 'agent', label: 'Agent 配置', icon: Settings },
  { id: 'tools', label: '工具管理', icon: Wrench }, { id: 'mcp', label: 'MCP 服务', icon: Server },
  { id: 'searxng', label: 'SearXNG', icon: Globe },
  { id: 'appearance', label: '外观', icon: Palette }, { id: 'logs', label: '日志与数据', icon: FileText },
];

export function SettingsModal(): React.JSX.Element | null {
  const settingsOpen = useUIStore((s) => s.settingsOpen);
  const closeSettings = useUIStore((s) => s.closeSettings);
  const theme = useUIStore((s) => s.theme);
  const setTheme = useUIStore((s) => s.setTheme);
  const [tab, setTab] = useState<SettingsTab>('llm');
  if (!settingsOpen) return null;

  return (
    <Dialog open onClose={closeSettings} maxWidth="md" fullWidth slotProps={{ paper: { sx: { maxWidth: 640, height: '80vh', borderRadius: 3, display: 'flex', flexDirection: 'column' } } }}>
      <Stack direction="row" sx={{ px: 2.5, py: 1.5, borderBottom: 1, borderColor: 'divider', justifyContent: 'space-between', alignItems: 'center' }}>
        <Typography variant="h6">设置</Typography>
        <IconButton size="small" onClick={closeSettings}><X size={14} /></IconButton>
      </Stack>
      <Box sx={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
        <Tabs orientation="vertical" value={tab} onChange={(_, v) => setTab(v)}
          sx={{
            borderRight: 1, borderColor: 'divider', minWidth: 130,
            '& .MuiTab-root': {
              alignItems: 'center', fontSize: 12,
              textTransform: 'none', gap: '2px', px: 2, justifyContent: 'flex-start',
              minHeight: 40,
            },
            '& .MuiTab-iconWrapper': { display: 'flex', alignItems: 'center', marginRight: 0 },
          }}
        >
          {TABS.map((t) => <Tab key={t.id} value={t.id} label={t.label} icon={<t.icon size={14} />} iconPosition="start" />)}
        </Tabs>
        <Box sx={{ flex: 1, overflowY: 'auto', p: 2.5 }}>
          {tab === 'workspace' && <WorkspaceSettings />}
          {tab === 'llm' && <LLMSettings />}
          {tab === 'agent' && <AgentSettings />}
          {tab === 'tools' && <ToolsSettings />}
          {tab === 'mcp' && <MCPSettings />}
          {tab === 'searxng' && <SearXNGSettings />}
          {tab === 'appearance' && <AppearanceSettings theme={theme} setTheme={setTheme} />}
          {tab === 'logs' && <LogsSettings />}
        </Box>
      </Box>
    </Dialog>
  );
}

function useConfig<T>(key: string, defaultValue: T): [T, (v: T) => void] {
  const [value, setValue] = useState<T>(defaultValue);
  useEffect(() => { if (window.metona?.config?.get) window.metona.config.get(key).then((v) => { if (v != null) setValue(v as T); }).catch((err) => { console.error('[SettingsModal]', err); }); }, [key]);
  const set = useCallback((v: T) => { setValue(v); window.metona?.config?.set(key, v).catch((err) => { console.error('[SettingsModal]', err); }); }, [key]);
  return [value, set];
}

const PROVIDER_URLS: Record<string, string> = { deepseek: 'https://api.deepseek.com', agnes: 'https://apihub.agnes-ai.com/v1', ollama: 'http://localhost:11434' };

function WorkspaceSettings() {
  const [workspacePath, setWorkspacePath] = useConfig('workspace.path', '');
  // 切换工作空间的中间状态
  const [pendingPath, setPendingPath] = useState<string | null>(null);
  const [checkResult, setCheckResult] = useState<MetonaWorkspaceCheckResult | null>(null);
  const [checking, setChecking] = useState(false);
  const [inheritSoul, setInheritSoul] = useState(true);
  const [inheritAgents, setInheritAgents] = useState(true);
  const [inheritUsers, setInheritUsers] = useState(true);
  const [applying, setApplying] = useState(false);
  const [showRestartDialog, setShowRestartDialog] = useState(false);

  const currentPath = workspacePath;

  const handleSelect = async () => {
    if (!window.metona?.app?.selectFolder) return;
    const r = await window.metona.app.selectFolder(currentPath || undefined);
    if (!r.canceled && r.path) {
      // 立即校验新路径
      setPendingPath(r.path);
      setChecking(true);
      setCheckResult(null);
      try {
        const result = await window.metona.workspace.check(r.path);
        setCheckResult(result);
      } catch (err) {
        console.error('[SettingsModal]', err);
        setCheckResult({ valid: false, reason: (err as Error).message });
      } finally {
        setChecking(false);
      }
    }
  };

  const handleApply = async () => {
    if (!pendingPath || !checkResult?.valid) return;
    setApplying(true);
    try {
      // 如果勾选了继承文件，从旧工作空间复制到新工作空间
      const filesToInherit: string[] = [];
      if (inheritSoul) filesToInherit.push('SOUL.md');
      if (inheritAgents) filesToInherit.push('AGENTS.md');
      if (inheritUsers) filesToInherit.push('USERS.md');

      if (filesToInherit.length > 0 && currentPath) {
        try {
          await window.metona.workspace.inheritFiles({
            targetPath: pendingPath,
            sourcePath: currentPath,
            files: filesToInherit,
          });
        } catch (err) {
          console.error('[SettingsModal] Inherit failed:', err);
        }
      }

      // 保存新路径到配置
      setWorkspacePath(pendingPath);
      // 弹出重启确认对话框
      setShowRestartDialog(true);
      // 清理中间状态
      setPendingPath(null);
      setCheckResult(null);
    } catch (err) {
      console.error('[SettingsModal]', err);
    } finally {
      setApplying(false);
    }
  };

  const handleCancel = () => {
    setPendingPath(null);
    setCheckResult(null);
  };

  const handleOpen = () => { if (workspacePath) window.metona?.app?.showItemInFolder(workspacePath); };

  return (
    <Stack spacing={2}>
      <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>工作空间</Typography>
      <Typography variant="body2" sx={{ color: 'text.secondary' }}>工作空间是 Metona 的组织核心，包含 SOUL.md、AGENTS.md、MEMORY.md、USERS.md 四个必需文件。</Typography>
      <Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
        <TextField size="small" value={workspacePath} onChange={(e) => setWorkspacePath(e.target.value)} placeholder="~/MetonaWorkspaces/default/" sx={{ flex: 1 }} />
        <Button variant="outlined" size="small" onClick={handleSelect}>选择文件夹</Button>
      </Stack>
      {workspacePath && (
        <Button variant="text" size="small" onClick={handleOpen} sx={{ alignSelf: 'flex-start', fontSize: 11, color: 'text.secondary' }}>
          📂 在文件管理器中打开
        </Button>
      )}

      {/* 校验中状态 */}
      {checking && (
        <Stack direction="row" spacing={1} sx={{ alignItems: 'center', py: 1 }}>
          <CircularProgress size={14} />
          <Typography variant="caption" sx={{ color: 'text.secondary' }}>正在校验工作空间...</Typography>
        </Stack>
      )}

      {/* 校验失败 */}
      {pendingPath && checkResult && !checkResult.valid && (
        <Alert severity="error" sx={{ py: 0.5 }}>
          <Typography variant="caption">路径无效：{checkResult.reason}</Typography>
          <Stack direction="row" spacing={1} sx={{ mt: 1 }}>
            <Button size="small" onClick={handleCancel}>取消</Button>
          </Stack>
        </Alert>
      )}

      {/* 校验成功 — 显示工作空间状态 + 继承选项 */}
      {pendingPath && checkResult?.valid && (
        <Stack spacing={1.5} sx={{ p: 1.5, borderRadius: 1.5, bgcolor: 'background.default', border: '1px solid', borderColor: 'divider' }}>
          <Typography variant="caption" sx={{ fontWeight: 600, color: 'text.primary' }}>
            目标工作空间：{checkResult.path}
          </Typography>

          {checkResult.isNewWorkspace ? (
            <Alert severity="info" sx={{ py: 0.5, '& .MuiAlert-message': { fontSize: 12 } }}>
              新工作空间 — 切换后将自动创建 4 个必需文件（SOUL.md、AGENTS.md、MEMORY.md、USERS.md）
            </Alert>
          ) : checkResult.missingFiles && checkResult.missingFiles.length > 0 ? (
            <Alert severity="warning" sx={{ py: 0.5, '& .MuiAlert-message': { fontSize: 12 } }}>
              已有目录但缺少 {checkResult.missingFiles.length} 个文件：{checkResult.missingFiles.join(', ')}。缺失文件将自动创建。
            </Alert>
          ) : (
            <Alert severity="success" sx={{ py: 0.5, '& .MuiAlert-message': { fontSize: 12 } }}>
              已有工作空间 — 4 个必需文件均已就绪，将直接加载现有配置。
            </Alert>
          )}

          {/* 继承选项（仅当当前有工作空间且新工作空间需要创建文件时显示） */}
          {currentPath && currentPath !== pendingPath && (checkResult.isNewWorkspace || (checkResult.missingFiles && checkResult.missingFiles.length > 0)) && (
            <Stack spacing={0.5} sx={{ mt: 0.5 }}>
              <Typography variant="caption" sx={{ fontWeight: 600, color: 'text.secondary' }}>
                从当前工作空间继承基础设置：
              </Typography>
              <FormControlLabel
                control={<Checkbox size="small" checked={inheritSoul} onChange={(e) => setInheritSoul(e.target.checked)} />}
                label={<Typography variant="caption">SOUL.md（身份与角色定义）</Typography>}
              />
              <FormControlLabel
                control={<Checkbox size="small" checked={inheritAgents} onChange={(e) => setInheritAgents(e.target.checked)} />}
                label={<Typography variant="caption">AGENTS.md（行为规则）</Typography>}
              />
              <FormControlLabel
                control={<Checkbox size="small" checked={inheritUsers} onChange={(e) => setInheritUsers(e.target.checked)} />}
                label={<Typography variant="caption">USERS.md（用户画像）</Typography>}
              />
              <Typography variant="caption" sx={{ color: 'text.disabled', fontSize: 10, pl: 3 }}>
                MEMORY.md 不继承（记忆与工作空间项目上下文绑定）
              </Typography>
            </Stack>
          )}

          <Stack direction="row" spacing={1} sx={{ mt: 1 }}>
            <Button
              variant="contained"
              size="small"
              onClick={handleApply}
              disabled={applying}
              startIcon={applying ? <CircularProgress size={12} /> : undefined}
            >
              {applying ? '应用中...' : '确认切换'}
            </Button>
            <Button variant="outlined" size="small" onClick={handleCancel} disabled={applying}>
              取消
            </Button>
          </Stack>
        </Stack>
      )}

      <Typography variant="caption" sx={{ color: 'text.disabled' }}>修改工作空间路径后需重启应用生效。</Typography>

      {/* 重启确认对话框 */}
      <Dialog open={showRestartDialog} onClose={() => setShowRestartDialog(false)} maxWidth="xs" fullWidth>
        <DialogTitle sx={{ fontSize: 14 }}>工作空间已切换</DialogTitle>
        <DialogContent>
          <Typography variant="body2" sx={{ color: 'text.secondary' }}>
            工作空间已更新为：
          </Typography>
          <Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 12, mt: 0.5, p: 1, borderRadius: 1, bgcolor: 'background.default' }}>
            {workspacePath}
          </Typography>
          <Typography variant="body2" sx={{ mt: 1.5, color: 'text.secondary' }}>
            需要重启应用以加载新工作空间的配置和文件。是否立即重启？
          </Typography>
        </DialogContent>
        <DialogActions>
          <Button size="small" onClick={() => setShowRestartDialog(false)}>稍后手动重启</Button>
          <Button size="small" variant="contained" color="primary" onClick={() => window.metona?.app?.restart()}>立即重启</Button>
        </DialogActions>
      </Dialog>
    </Stack>
  );
}

function LLMSettings() {
  const [provider, setProvider] = useConfig('llm.provider', '');
  const [model, setModel] = useConfig('llm.model', '');
  const [apiKey, setApiKey] = useConfig('llm.apiKey', '');
  const [baseURL, setBaseURL] = useConfig('llm.baseURL', '');
  const [numCtx, setNumCtx] = useConfig('ollama.numCtx', null as number | null);
  const [showKey, setShowKey] = useState(false);

  // 同步 Provider/Model 到 Agent Store（含 contextWindow）
  useEffect(() => {
    useAgentStore.getState().setProvider(provider, model);
  }, [provider, model]);

  // Ollama numCtx 变化时同步 contextWindow
  useEffect(() => {
    if (provider === 'ollama' && numCtx != null && numCtx > 0) {
      useAgentStore.setState({ contextWindow: numCtx });
    }
  }, [provider, numCtx]);

  // 切换 Provider 时自动填充默认 URL + 清空 apiKey（不同 Provider 的 key 不通用）
  const handleProviderChange = (newProvider: string) => {
    const oldProvider = provider;
    setProvider(newProvider);
    // 切换 Provider 时清空 apiKey（不同 Provider 的 key 格式不同，避免用旧 key 调用新 API 导致 401）
    // Ollama 不需要 apiKey，也一并清空
    if (oldProvider !== newProvider && apiKey) {
      setApiKey('');
    }
    // 自动填充默认 URL（仅在 URL 为空或与旧 provider 默认 URL 匹配时）
    const currentUrl = baseURL.trim();
    const isDefaultUrl = Object.values(PROVIDER_URLS).includes(currentUrl);
    if (isDefaultUrl || !currentUrl) {
      setBaseURL(PROVIDER_URLS[newProvider] ?? '');
    }
  };

  return (
    <Stack spacing={2}>
      <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>LLM 配置</Typography>
      <FormControl size="small"><InputLabel>Provider</InputLabel>
        <Select value={provider} label="Provider" onChange={(e) => handleProviderChange(e.target.value)}>
          <MenuItem value="deepseek">DeepSeek</MenuItem>
          <MenuItem value="agnes">Agnes AI</MenuItem>
          <MenuItem value="ollama">Ollama (本地)</MenuItem>
        </Select>
      </FormControl>
      <TextField size="small" label="API Base URL" value={baseURL} onChange={(e) => setBaseURL(e.target.value)} placeholder="如 https://api.deepseek.com" />
      <TextField size="small" label="模型名称" value={model} onChange={(e) => setModel(e.target.value)} placeholder="如 deepseek-v4-pro、qwen3:latest" />
      {provider !== 'ollama' && (
        <>
          <TextField size="small" label="API Key" type={showKey ? 'text' : 'password'} value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder="sk-..."
            slotProps={{ input: { endAdornment: <IconButton size="small" onClick={() => setShowKey(!showKey)}>{showKey ? <EyeOff size={14} /> : <Eye size={14} />}</IconButton> } }}
          />
          {!apiKey && (
            <Typography variant="caption" sx={{ color: 'warning.main', fontSize: 11 }}>
              ⚠ 请输入 {PROVIDER_LABELS[provider] ?? provider} 的 API Key，否则发送消息会报错
            </Typography>
          )}
        </>
      )}
      {provider === 'ollama' && (
        <TextField
          size="small"
          label="上下文长度 (num_ctx)"
          type="number"
          value={numCtx ?? ''}
          onChange={(e) => {
            const v = e.target.value;
            setNumCtx(v === '' ? null : Number(v));
          }}
          placeholder="默认由模型决定（如 2048、4096、128000）"
          slotProps={{ htmlInput: { min: 512, step: 512 } }}
        />
      )}
    </Stack>
  );
}

function AgentSettings() {
  const [maxIter, setMaxIter] = useConfig('agent.maxIterations', 20);
  const [timeout, setTimeout_] = useConfig('agent.totalTimeoutMs', 600000);
  const [thinking, setThinking] = useConfig('agent.enableThinking', true);
  const [thinkingEffort, setThinkingEffort] = useConfig('agent.thinkingEffort', 'high');
  const [confirmTimeout, setConfirmTimeout] = useConfig('agent.confirmationTimeoutMs', 120000);

  const MAX_ITER_OPTIONS = [10, 20, 50, 85, 128, 256, 512];

  return (
    <Stack spacing={2}>
      <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>Agent 配置</Typography>
      <FormControl size="small">
        <InputLabel>最大迭代次数</InputLabel>
        <Select value={maxIter} label="最大迭代次数" onChange={(e) => setMaxIter(e.target.value as number)}>
          {MAX_ITER_OPTIONS.map((n) => (
            <MenuItem key={n} value={n}>{n}</MenuItem>
          ))}
        </Select>
      </FormControl>
      <TextField
        size="small"
        label="总超时（秒）"
        type="number"
        value={timeout / 1000}
        onChange={(e) => {
          const v = Number(e.target.value);
          if (v >= 120 && v <= 3600) setTimeout_(v * 1000);
        }}
        slotProps={{ htmlInput: { min: 120, max: 3600, step: 30 } }}
      />
      <TextField
        size="small"
        label="工具确认超时（秒）"
        type="number"
        value={confirmTimeout / 1000}
        onChange={(e) => {
          const v = Number(e.target.value);
          if (v >= 30 && v <= 600) setConfirmTimeout(v * 1000);
        }}
        slotProps={{ htmlInput: { min: 30, max: 600, step: 10 } }}
        helperText="用户未响应工具确认时，超时自动视为拒绝（30~600 秒）"
      />
      <FormControlLabel control={<Checkbox checked={thinking} onChange={(e) => setThinking(e.target.checked)} size="small" />} label={<Typography variant="body2">启用思考模式</Typography>} />
      {thinking && (
        <FormControl size="small"><InputLabel>思考强度</InputLabel>
          <Select value={thinkingEffort} label="思考强度" onChange={(e) => setThinkingEffort(e.target.value)}>
            <MenuItem value="low">Low</MenuItem><MenuItem value="medium">Medium</MenuItem><MenuItem value="high">High</MenuItem><MenuItem value="max">Max</MenuItem>
          </Select>
        </FormControl>
      )}
    </Stack>
  );
}

function ToolsSettings() {
  const [tools, setTools] = useState<Array<{ name: string; description: string; riskLevel: string; requiresPermission: boolean; enabled: boolean }>>([]);
  const [autoExecList, setAutoExecList] = useState<string[]>([]);

  const loadTools = () => {
    if (window.metona?.tools?.list) {
      window.metona.tools.list().then((l) => setTools((l as MetonaToolInfo[]).map((t) => ({
        name: t.name, description: t.description, riskLevel: t.riskLevel,
        requiresPermission: t.requiresPermission, enabled: t.enabled,
      })))).catch((err) => { console.error('[SettingsModal]', err); });
    }
  };
  const loadAutoExec = () => {
    if (window.metona?.tool?.getAutoExecuteList) {
      window.metona.tool.getAutoExecuteList().then((r) => {
        if (r.success) setAutoExecList(r.data);
      }).catch((err) => { console.error('[SettingsModal]', err); });
    }
  };
  useEffect(() => { loadTools(); loadAutoExec(); }, []);

  const handleToggle = (name: string, enabled: boolean) => {
    setTools((p) => p.map((t) => t.name === name ? { ...t, enabled } : t));
    window.metona?.tools?.toggle(name, enabled).catch((err) => { console.error('[SettingsModal]', err); });
  };

  // 设置/取消自动执行
  const handleSetAutoExec = async (name: string, enabled: boolean) => {
    if (!window.metona?.tool?.setAutoExecute) return;
    const r = await window.metona.tool.setAutoExecute(name, enabled);
    if (r.success) {
      setAutoExecList((p) => enabled ? [...p, name] : p.filter((n) => n !== name));
    }
  };

  const riskColors: Record<string, 'success' | 'info' | 'warning' | 'error'> = { safe: 'success', low: 'info', medium: 'warning', high: 'error' };

  // 需要确认的工具（high/critical 或 requiresPermission）
  const needsConfirmTools = tools.filter((t) =>
    t.riskLevel === 'high' || t.riskLevel === 'critical' || t.requiresPermission,
  );
  // 需要确认但未设为自动执行的工具
  const pendingConfirmTools = needsConfirmTools.filter((t) => !autoExecList.includes(t.name));
  // 已设为自动执行的工具详情
  const autoExecToolDetails = autoExecList
    .map((name) => tools.find((t) => t.name === name))
    .filter((t): t is NonNullable<typeof t> => t !== undefined);

  return (
    <Stack spacing={2}>
      <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>工具管理</Typography>
      {tools.length === 0 ? <Typography variant="caption" sx={{ textAlign: 'center', py: 4, color: 'text.secondary' }}>加载中...</Typography> : tools.map((t) => (
        <Stack key={t.name} direction="row" sx={{ py: 1, px: 1.5, borderRadius: 1.5, bgcolor: 'secondary.main', justifyContent: 'space-between', alignItems: 'center' }}>
          <Stack direction="row" spacing={1} sx={{ minWidth: 0, flex: 1, alignItems: 'center' }}>
            <Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 12 }}>{t.name}</Typography>
            <Typography variant="caption" sx={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.description.slice(0, 40)}</Typography>
          </Stack>
          <Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
            <Chip label={t.riskLevel.toUpperCase()} size="small" color={riskColors[t.riskLevel]} variant="outlined" sx={{ height: 18, fontSize: 9 }} />
            <Checkbox checked={t.enabled} onChange={(e) => handleToggle(t.name, e.target.checked)} size="small" />
          </Stack>
        </Stack>
      ))}

      <Divider sx={{ my: 1 }} />

      {/* ===== 自动执行工具管理 ===== */}
      <Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'space-between' }}>
        <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>自动执行工具</Typography>
        <Chip label={`${autoExecList.length} 个`} size="small" color={autoExecList.length > 0 ? 'success' : 'default'} variant="outlined" sx={{ height: 18, fontSize: 10 }} />
      </Stack>
      <Typography variant="caption" sx={{ color: 'text.secondary', lineHeight: 1.5 }}>
        已设为自动执行的工具将跳过用户确认步骤，直接执行。此设置跨会话持久化。
      </Typography>

      {/* 已自动执行的工具列表 */}
      {autoExecToolDetails.length === 0 ? (
        <Typography variant="caption" sx={{ textAlign: 'center', py: 2, color: 'text.disabled', fontStyle: 'italic' }}>
          暂无自动执行工具
        </Typography>
      ) : (
        autoExecToolDetails.map((t) => (
          <Stack
            key={t.name}
            direction="row"
            sx={(theme) => ({
              py: 1, px: 1.5, borderRadius: 1.5,
              // 用主题 success 色的 12% 透明度做底，文字和按钮保持不透明
              bgcolor: alpha(theme.palette.success.main, 0.12),
              // 左侧绿色状态条，强化"已自动执行"视觉
              boxShadow: `inset 3px 0 0 ${theme.palette.success.main}`,
              justifyContent: 'space-between', alignItems: 'center',
            })}
          >
            <Stack direction="row" spacing={1} sx={{ alignItems: 'center', minWidth: 0, flex: 1 }}>
              <Box
                component="span"
                sx={{
                  width: 6, height: 6, borderRadius: '50%',
                  bgcolor: 'success.main', flexShrink: 0,
                }}
              />
              <Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 12, color: 'text.primary', fontWeight: 600 }}>
                {t.name}
              </Typography>
              <Chip label="自动" size="small" color="success" sx={{ height: 16, fontSize: 9 }} />
            </Stack>
            <Button
              size="small"
              color="error"
              variant="contained"
              sx={{ fontSize: 11, minWidth: 72, fontWeight: 600 }}
              onClick={() => handleSetAutoExec(t.name, false)}
            >
              取消自动
            </Button>
          </Stack>
        ))
      )}

      {/* 可设为自动执行的工具（需要确认但未设置） */}
      {pendingConfirmTools.length > 0 && (
        <>
          <Typography variant="caption" sx={{ fontWeight: 600, color: 'text.secondary', mt: 1 }}>
            可设为自动执行（当前需要确认）
          </Typography>
          {pendingConfirmTools.map((t) => (
            <Stack key={t.name} direction="row" sx={{ py: 1, px: 1.5, borderRadius: 1.5, bgcolor: 'secondary.main', border: '1px dashed', borderColor: 'divider', justifyContent: 'space-between', alignItems: 'center' }}>
              <Stack direction="row" spacing={1} sx={{ alignItems: 'center', minWidth: 0, flex: 1 }}>
                <Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 12, color: 'text.primary' }}>{t.name}</Typography>
                <Chip label={t.riskLevel.toUpperCase()} size="small" color={riskColors[t.riskLevel]} variant="outlined" sx={{ height: 16, fontSize: 9 }} />
              </Stack>
              <Button size="small" color="success" variant="contained" sx={{ fontSize: 11, minWidth: 72, fontWeight: 600 }} onClick={() => handleSetAutoExec(t.name, true)}>
                设为自动
              </Button>
            </Stack>
          ))}
        </>
      )}
    </Stack>
  );
}

function MCPSettings() {
  const [servers, setServers] = useState<Array<{ name: string; status: string; toolCount: number; error?: string }>>([]);
  const [showAdd, setShowAdd] = useState(false);
  const [newName, setNewName] = useState('');
  const [newCommand, setNewCommand] = useState('');
  const [newArgs, setNewArgs] = useState('');
  const loadServers = () => { if (window.metona?.mcp?.listServers) window.metona.mcp.listServers().then((l) => setServers(l as MetonaMCPServerStatus[])).catch((err) => { console.error('[SettingsModal]', err); }); };
  useEffect(() => { loadServers(); }, []);
  const handleAdd = async () => { if (!newName.trim() || !newCommand.trim()) return; await window.metona?.mcp?.addServer({ name: newName.trim(), transport: 'stdio', command: newCommand.trim(), args: newArgs.trim() ? newArgs.trim().split(/\s+/) : [], enabled: true }); setNewName(''); setNewCommand(''); setNewArgs(''); setShowAdd(false); loadServers(); };
  const statusColors: Record<string, string> = { connected: 'success.main', connecting: 'warning.main', disconnected: 'text.secondary', error: 'error.main' };

  return (
    <Stack spacing={2}>
      <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>MCP 服务</Typography>
      {servers.length === 0 ? <Typography variant="caption" sx={{ textAlign: 'center', py: 4, color: 'text.secondary' }}>暂无 MCP 服务</Typography> : servers.map((s) => (
        <Stack key={s.name} direction="row" sx={{ py: 1, px: 1.5, borderRadius: 1.5, bgcolor: 'secondary.main', justifyContent: 'space-between', alignItems: 'center' }}>
          <Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
            <Box sx={{ width: 8, height: 8, borderRadius: '50', bgcolor: statusColors[s.status] }} />
            <Typography variant="body2" sx={{ fontWeight: 500 }}>{s.name}</Typography>
            <Typography variant="caption" sx={{ color: statusColors[s.status] }}>{s.status}</Typography>
            {s.toolCount > 0 && <Typography variant="caption" sx={{ color: 'text.secondary' }}>{s.toolCount} 工具</Typography>}
          </Stack>
          <Stack direction="row" spacing={0.5}>
            <Button size="small" sx={{ fontSize: 10, minWidth: 40 }} onClick={async () => { await window.metona?.mcp?.toggleServer(s.name, s.status !== 'connected'); loadServers(); }}>{s.status === 'connected' ? '断开' : '连接'}</Button>
            <Button size="small" color="error" sx={{ fontSize: 10, minWidth: 40 }} onClick={async () => { if (confirm(`确定移除 "${s.name}"？`)) { await window.metona?.mcp?.removeServer(s.name); loadServers(); } }}>移除</Button>
          </Stack>
        </Stack>
      ))}
      {showAdd ? (
        <Stack spacing={1} sx={{ p: 1.5, borderRadius: 1.5, bgcolor: 'secondary.main', border: '1px solid', borderColor: 'divider' }}>
          <TextField size="small" value={newName} onChange={(e) => setNewName(e.target.value)} placeholder="服务名称" />
          <TextField size="small" value={newCommand} onChange={(e) => setNewCommand(e.target.value)} placeholder="命令路径" />
          <TextField size="small" value={newArgs} onChange={(e) => setNewArgs(e.target.value)} placeholder="参数 (空格分隔)" />
          <Stack direction="row" spacing={1}>
            <Button variant="contained" size="small" onClick={handleAdd} disabled={!newName.trim() || !newCommand.trim()} sx={{ flex: 1 }}>添加</Button>
            <Button variant="outlined" size="small" onClick={() => setShowAdd(false)} sx={{ flex: 1 }}>取消</Button>
          </Stack>
        </Stack>
      ) : <Button variant="outlined" fullWidth size="small" onClick={() => setShowAdd(true)}>+ 添加 MCP 服务</Button>}
    </Stack>
  );
}

function SearXNGSettings() {
  // ===== 12 项配置（useConfig 实时持久化） =====
  const [enabled, setEnabled] = useConfig('searxng.enabled', false);
  const [url, setUrl] = useConfig('searxng.url', '');
  const [engines, setEngines] = useConfig('searxng.engines', '');
  const [language, setLanguage] = useConfig('searxng.language', 'zh-CN');
  const [safesearch, setSafesearch] = useConfig('searxng.safesearch', 1);
  const [timeRange, setTimeRange] = useConfig('searxng.time_range', '');
  const [maxResults, setMaxResults] = useConfig('searxng.max_results', 0);
  const [authKey, setAuthKey] = useConfig('searxng.auth_key', '');
  const [authType, setAuthType] = useConfig('searxng.auth_type', 'bearer');
  const [format, setFormat] = useConfig('searxng.format', 'json');
  const [fetchCount, setFetchCount] = useConfig('searxng.fetch_count', 0);
  const [fetchMode, setFetchMode] = useConfig('searxng.fetch_mode', 'sequential');

  const [showKey, setShowKey] = useState(false);
  const [testing, setTesting] = useState(false);
  const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null);

  const urlError = !!url && !/^https?:\/\//.test(url);

  const handleTest = async () => {
    if (!url.trim() || urlError) return;
    setTesting(true);
    setTestResult(null);
    try {
      const result = await window.metona?.searxng?.testConnection(url.trim(), authKey, authType);
      if (result?.success) {
        setTestResult({ success: true, message: `连接成功（${result.latencyMs}ms）` });
      } else {
        setTestResult({ success: false, message: result?.error || `连接失败（HTTP ${result?.statusCode}）` });
      }
    } catch (e) {
      setTestResult({ success: false, message: (e as Error).message });
    }
    setTesting(false);
  };

  return (
    <Stack spacing={2}>
      {/* 标题 + 状态徽章 */}
      <Stack direction="row" sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
        <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>SearXNG 元搜索</Typography>
        <Chip label={enabled ? '已启用' : '未启用'} size="small" color={enabled ? 'success' : 'default'} variant={enabled ? 'filled' : 'outlined'} />
      </Stack>
      <Typography variant="body2" sx={{ color: 'text.secondary' }}>
        SearXNG 是开源元搜索引擎，支持 70+ 搜索引擎聚合。启用后替代内置四引擎搜索通道，未启用时回退到 Bing + 百度 + 搜狗 + 360 搜索。
      </Typography>

      {/* 启用开关 */}
      <FormControlLabel control={<Switch checked={enabled} onChange={(e) => setEnabled(e.target.checked)} size="small" />} label={<Typography variant="body2">启用 SearXNG</Typography>} />

      <Divider />

      {/* API 地址 + 连接测试 */}
      <Stack direction="row" spacing={1} sx={{ alignItems: 'flex-start' }}>
        <TextField
          size="small"
          label="API 地址"
          value={url}
          onChange={(e) => setUrl(e.target.value)}
          placeholder="如 https://searxng.example.com"
          error={urlError}
          helperText={urlError ? '需以 http:// 或 https:// 开头' : '实例根地址（不含 /search 路径）'}
          sx={{ flex: 1 }}
        />
        <Button variant="outlined" size="small" onClick={handleTest} disabled={!url.trim() || urlError || testing} sx={{ mt: 0.5, minWidth: 90, height: 40 }}>
          {testing ? <CircularProgress size={14} /> : '测试连接'}
        </Button>
      </Stack>

      {/* 测试结果 */}
      {testResult && (
        <Alert severity={testResult.success ? 'success' : 'error'} sx={{ py: 0.5, '& .MuiAlert-message': { fontSize: 12 } }}>
          {testResult.message}
        </Alert>
      )}

      {/* 搜索引擎 */}
      <TextField
        size="small"
        label="搜索引擎（逗号分隔）"
        value={engines}
        onChange={(e) => setEngines(e.target.value)}
        placeholder="如 google,bing,duckduckgo（留空使用实例默认）"
      />

      {/* 语言 + 安全搜索 */}
      <Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 2 }}>
        <FormControl size="small"><InputLabel>语言</InputLabel>
          <Select value={language} label="语言" onChange={(e) => setLanguage(e.target.value)}>
            <MenuItem value="zh-CN">简体中文</MenuItem>
            <MenuItem value="zh-TW">繁體中文</MenuItem>
            <MenuItem value="en">English</MenuItem>
            <MenuItem value="ja">日本語</MenuItem>
            <MenuItem value="ko">한국어</MenuItem>
            <MenuItem value="auto">自动检测</MenuItem>
          </Select>
        </FormControl>
        <FormControl size="small"><InputLabel>安全搜索</InputLabel>
          <Select value={safesearch} label="安全搜索" onChange={(e) => setSafesearch(e.target.value as number)}>
            <MenuItem value={0}>关闭</MenuItem>
            <MenuItem value={1}>中等</MenuItem>
            <MenuItem value={2}>严格</MenuItem>
          </Select>
        </FormControl>
      </Box>

      {/* 时间范围 + 返回格式 */}
      <Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 2 }}>
        <FormControl size="small"><InputLabel>时间范围</InputLabel>
          <Select value={timeRange} label="时间范围" onChange={(e) => setTimeRange(e.target.value)}>
            <MenuItem value="">不限</MenuItem>
            <MenuItem value="day">一天</MenuItem>
            <MenuItem value="week">一周</MenuItem>
            <MenuItem value="month">一月</MenuItem>
            <MenuItem value="year">一年</MenuItem>
          </Select>
        </FormControl>
        <FormControl size="small"><InputLabel>返回格式</InputLabel>
          <Select value={format} label="返回格式" onChange={(e) => setFormat(e.target.value)}>
            <MenuItem value="json">JSON（结构化解析）</MenuItem>
            <MenuItem value="html">HTML（原始网页）</MenuItem>
          </Select>
        </FormControl>
      </Box>

      {/* 最大结果数 + 自动抓取条数 */}
      <Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 2 }}>
        <TextField
          size="small"
          label="最大结果数"
          type="number"
          value={maxResults}
          onChange={(e) => setMaxResults(Number(e.target.value))}
          placeholder="0 表示使用默认"
          slotProps={{ htmlInput: { min: 0, max: 50 } }}
        />
        <TextField
          size="small"
          label="自动抓取条数"
          type="number"
          value={fetchCount}
          onChange={(e) => setFetchCount(Number(e.target.value))}
          placeholder="0 表示由 AI 决定"
          slotProps={{ htmlInput: { min: 0, max: 8 } }}
        />
      </Box>

      {/* 抓取类型 */}
      <FormControl size="small"><InputLabel>抓取类型</InputLabel>
        <Select value={fetchMode} label="抓取类型" onChange={(e) => setFetchMode(e.target.value)}>
          <MenuItem value="sequential">顺序抓取</MenuItem>
          <MenuItem value="random">随机抓取</MenuItem>
        </Select>
      </FormControl>

      <Divider />

      {/* 认证设置 */}
      <Typography variant="caption" sx={{ fontWeight: 600, color: 'text.secondary' }}>认证设置</Typography>
      <Box sx={{ display: 'grid', gridTemplateColumns: '1fr 2fr', gap: 2 }}>
        <FormControl size="small"><InputLabel>认证类型</InputLabel>
          <Select value={authType} label="认证类型" onChange={(e) => setAuthType(e.target.value)}>
            <MenuItem value="bearer">Bearer Token</MenuItem>
            <MenuItem value="basic">Basic Auth</MenuItem>
          </Select>
        </FormControl>
        <TextField
          size="small"
          label={authType === 'bearer' ? 'Token' : '用户名:密码'}
          type={showKey ? 'text' : 'password'}
          value={authKey}
          onChange={(e) => setAuthKey(e.target.value)}
          placeholder={authType === 'bearer' ? '访问令牌原值' : 'username:password'}
          slotProps={{ input: { endAdornment: <IconButton size="small" onClick={() => setShowKey(!showKey)}>{showKey ? <EyeOff size={14} /> : <Eye size={14} />}</IconButton> } }}
        />
      </Box>
      <Typography variant="caption" sx={{ color: 'text.disabled' }}>
        {authType === 'bearer'
          ? 'Bearer: 直接填写令牌原值，原样透传到 Authorization 头。建议配合 HTTPS 使用。'
          : 'Basic: 填写 username:password 明文串，系统自动 Base64 编码。必须配合 HTTPS 使用。'}
      </Typography>
    </Stack>
  );
}

function AppearanceSettings({ theme, setTheme }: { theme: ThemeMode; setTheme: (t: ThemeMode) => void }) {
  return (
    <Stack spacing={2}>
      <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>外观</Typography>
      <Box><Typography variant="caption" sx={{ color: 'text.secondary', mb: 1, display: 'block' }}>主题</Typography>
        <Stack direction="row" spacing={1}>
          {(['dark', 'light', 'auto'] as ThemeMode[]).map((t) => <Button key={t} variant={theme === t ? 'contained' : 'outlined'} size="small" onClick={() => setTheme(t)}>{t === 'dark' ? '深色' : t === 'light' ? '浅色' : '跟随系统'}</Button>)}
        </Stack>
      </Box>
    </Stack>
  );
}

function LogsSettings() {
  const [logLevel, setLogLevel] = useConfig('logging.level', 'info');
  const [clearing, setClearing] = useState<string | null>(null);
  const handleExport = async () => { if (!window.metona?.data?.exportData) return; const r = await window.metona.data.exportData(); if (r.success && r.data) { const b = new Blob([JSON.stringify(r.data, null, 2)], { type: 'application/json' }); const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = `metona-export-${Date.now()}.json`; a.click(); } };
  const handleClear = async (type: 'sessions' | 'memories' | 'auditLogs') => { const labels: Record<string, string> = { sessions: '所有会话', memories: '所有记忆', auditLogs: '审计日志' }; if (!confirm(`确定清理${labels[type]}？`)) return; setClearing(type); try { let r; if (type === 'sessions') r = await window.metona?.data?.clearSessions(); else if (type === 'memories') r = await window.metona?.data?.clearMemories(); else r = await window.metona?.data?.clearAuditLogs(); if (r?.success) alert(`${labels[type]}已清理`); else alert(`失败: ${r?.error}`); } catch (e) { alert(`失败: ${(e as Error).message}`); } setClearing(null); };

  return (
    <Stack spacing={2}>
      <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>日志与数据</Typography>
      <FormControl size="small"><InputLabel>日志级别</InputLabel>
        <Select value={logLevel} label="日志级别" onChange={(e) => setLogLevel(e.target.value)}>
          <MenuItem value="debug">DEBUG</MenuItem><MenuItem value="info">INFO</MenuItem><MenuItem value="warn">WARN</MenuItem><MenuItem value="error">ERROR</MenuItem>
        </Select>
      </FormControl>
      <Divider />
      <Typography variant="caption" sx={{ fontWeight: 600, color: 'text.secondary' }}>数据管理</Typography>
      <Button variant="outlined" fullWidth size="small" onClick={handleExport}>📦 导出全部数据（JSON）</Button>
      <Button variant="outlined" fullWidth size="small" color="error" onClick={() => handleClear('sessions')} disabled={clearing !== null}>{clearing === 'sessions' ? '清理中...' : '🗑️ 清理所有会话'}</Button>
      <Button variant="outlined" fullWidth size="small" color="error" onClick={() => handleClear('memories')} disabled={clearing !== null}>{clearing === 'memories' ? '清理中...' : '🗑️ 清理所有记忆'}</Button>
      <Button variant="outlined" fullWidth size="small" color="error" onClick={() => handleClear('auditLogs')} disabled={clearing !== null}>{clearing === 'auditLogs' ? '清理中...' : '🗑️ 清理审计日志'}</Button>
    </Stack>
  );
}
