import { useState, useEffect, useCallback, type ReactNode } from 'react'
import {
  Settings, Server, Cpu, Bot, Clock, Calendar, Wrench, Brain, Image,
  Users, Save, X, Plus, Trash2, AlertTriangle, Loader2, Wifi,
  CheckCircle, Plug, Radio, RefreshCw, UserCheck, Pencil,
} from 'lucide-react'

// ── Types ──────────────────────────────────────────────
interface ProviderConfig { type: string; base_url: string; api_key: string; extra_headers: Record<string, string>; llm_timeout_secs: number; memory_maintenance_timeout_secs: number }
interface ModelConfig { model_id: string; temperature?: number; max_tokens?: number; context_window_tokens?: number }
interface AgentConfig { provider: string; model: string; max_tool_iterations: number; tool_result_max_chars: number; context_tool_result_trim_chars: number }
interface GatewayConfig { host: string; port: number; show_tool_results: boolean; agent_prompt_reinject_every: number; max_concurrent_requests: number; session_ttl_hours?: number }
interface TimeConfig { timezone: string }
interface SchedulerConfig { enabled: boolean; tick_resolution_ms: number; worker_queue_capacity: number; misfire_policy: 'skip' | 'catch_up'; jobs?: any[] }
interface SkillsConfig { enabled: boolean; sources: string[]; max_index_chars: number; max_listed_skills: number }
interface TaskConfig { enabled: boolean; max_execution_secs: number; explore_max_execution_secs: number; ttl_hours: number; allowed_tools: string[] }
interface ToolsConfig { disabled: string[]; task: TaskConfig }
interface MemoryMaintenanceConfig { max_merge_ratio: number; min_memories_to_keep: number; max_merge_per_group: number }
interface ImageContextConfig { max_images_in_context: number; max_image_age_rounds: number }
interface SubagentsConfig { enabled: boolean; sources: string[] }
interface ClientConfig { gateway_url: string }
interface McpServerConfig {
  name?: string
  type: 'stdio' | 'streamableHttp' | 'http'
  is_active: boolean
  command?: string
  args?: string[]
  env?: Record<string, string>
  cwd?: string
  base_url?: string
  headers?: Record<string, string>
  description?: string
}

interface SkillItem {
  name: string
  description: string
  source: string
  path: string
  disabled_in_scopes: string[]
}

interface SkillListResponse {
  skills_system_enabled: boolean
  total: number
  skills: SkillItem[]
}

interface SubagentItem {
  name: string
  description: string
  source: string
  disabled_in_scopes: string[]
}

interface SubagentListResponse {
  subagents_system_enabled: boolean
  total: number
  subagents: SubagentItem[]
}

interface ExpertsConfig { enabled: boolean; sources: string[] }
interface ExpertItem {
  name: string
  description: string
  source: string
  path?: string
  body?: string
  disabled_in_scopes: string[]
}
interface ExpertListResponse {
  experts_system_enabled: boolean
  total: number
  experts: ExpertItem[]
}

interface McpServerStatus {
  key: string
  name: string
  transport_type: string
  is_active: boolean
  connected: boolean
  tool_count: number
  error?: string
}

interface McpStatusResponse {
  enabled: boolean
  total_servers: number
  connected_servers: number
  failed_servers: number
  total_tools: number
  servers: McpServerStatus[]
}
interface AppConfig {
  providers: Record<string, ProviderConfig>
  models: Record<string, ModelConfig>
  agents: Record<string, AgentConfig>
  time: TimeConfig
  gateway: GatewayConfig
  scheduler: SchedulerConfig
  skills: SkillsConfig
  tools: ToolsConfig
  memory_maintenance: MemoryMaintenanceConfig
  image_context: ImageContextConfig
  subagents: SubagentsConfig
  experts: ExpertsConfig
  client: ClientConfig
  channels: Record<string, any>
  mcpServers: Record<string, McpServerConfig>
}

interface ConfigPageProps {
  onClose: () => void
  onSaveConnection?: (host: string, port: number) => void
  initialTab?: TabId
}

type TabId = 'connection' | 'gateway' | 'providers' | 'models' | 'agents' | 'time' | 'scheduler' | 'skills' | 'tools' | 'memory' | 'image' | 'subagents' | 'experts' | 'mcp' | 'channels'

const TABS: { id: TabId; label: string; icon: typeof Settings }[] = [
  { id: 'providers', label: '服务商', icon: Cpu },
  { id: 'models', label: '模型', icon: Brain },
  { id: 'agents', label: '代理', icon: Bot },
  { id: 'mcp', label: 'MCP 服务器', icon: Plug },
  { id: 'skills', label: '技能', icon: Wrench },
  { id: 'subagents', label: '子代理', icon: Bot },
  { id: 'experts', label: '专家', icon: UserCheck },
  { id: 'channels', label: '渠道', icon: Radio },
  { id: 'tools', label: '工具', icon: Settings },
  { id: 'memory', label: '记忆维护', icon: Users },
  { id: 'scheduler', label: '调度器', icon: Calendar },
  { id: 'image', label: '图片上下文', icon: Image },
  { id: 'time', label: '时间', icon: Clock },
  { id: 'connection', label: '连接', icon: Wifi },
  { id: 'gateway', label: '网关', icon: Server },
]

// ── Shared UI primitives ───────────────────────────────
function Field({ label, children, hint }: { label: string; children: ReactNode; hint?: string }) {
  return (
    <div className="space-y-1.5">
      <label className="block text-[13px] font-medium text-[var(--text-secondary)]">{label}</label>
      {children}
      {hint && <p className="text-xs text-[var(--text-muted)]">{hint}</p>}
    </div>
  )
}

const inputCls = "w-full px-3 py-2 rounded-lg bg-[var(--bg-tertiary)] border border-[var(--border-color)] text-[var(--text-primary)] text-sm placeholder:text-[var(--text-muted)] focus:outline-none focus:border-[var(--accent-cyan)] focus:ring-1 focus:ring-[var(--focus-ring)] transition-colors"
const selectCls = inputCls

const TIMEZONE_OPTIONS: { value: string; label: string }[] = [
  { value: 'Asia/Shanghai', label: 'Asia/Shanghai (中国标准时间, UTC+8)' },
  { value: 'Asia/Tokyo', label: 'Asia/Tokyo (日本标准时间, UTC+9)' },
  { value: 'Asia/Seoul', label: 'Asia/Seoul (韩国标准时间, UTC+9)' },
  { value: 'Asia/Singapore', label: 'Asia/Singapore (新加坡时间, UTC+8)' },
  { value: 'Asia/Hong_Kong', label: 'Asia/Hong_Kong (香港时间, UTC+8)' },
  { value: 'Asia/Taipei', label: 'Asia/Taipei (台北时间, UTC+8)' },
  { value: 'Asia/Bangkok', label: 'Asia/Bangkok (曼谷时间, UTC+7)' },
  { value: 'Asia/Kolkata', label: 'Asia/Kolkata (印度标准时间, UTC+5:30)' },
  { value: 'Asia/Dubai', label: 'Asia/Dubai (海湾标准时间, UTC+4)' },
  { value: 'Europe/London', label: 'Europe/London (格林威治时间, UTC+0)' },
  { value: 'Europe/Paris', label: 'Europe/Paris (中欧时间, UTC+1)' },
  { value: 'Europe/Berlin', label: 'Europe/Berlin (中欧时间, UTC+1)' },
  { value: 'Europe/Moscow', label: 'Europe/Moscow (莫斯科时间, UTC+3)' },
  { value: 'America/New_York', label: 'America/New_York (美东时间, UTC-5)' },
  { value: 'America/Chicago', label: 'America/Chicago (美中时间, UTC-6)' },
  { value: 'America/Denver', label: 'America/Denver (美山地时间, UTC-7)' },
  { value: 'America/Los_Angeles', label: 'America/Los_Angeles (美太平洋时间, UTC-8)' },
  { value: 'Pacific/Auckland', label: 'Pacific/Auckland (新西兰时间, UTC+12)' },
  { value: 'Australia/Sydney', label: 'Australia/Sydney (澳东时间, UTC+10)' },
  { value: 'UTC', label: 'UTC (协调世界时)' },
]

function Toggle({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) {
  return (
    <button
      type="button"
      onClick={() => onChange(!checked)}
      className={`relative inline-flex h-6 w-11 shrink-0 rounded-full transition-colors duration-200 ${checked ? 'bg-[var(--accent-cyan)]' : 'bg-[var(--bg-hover)]'}`}
    >
      <span className={`absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white shadow-sm transition-transform duration-200 ${checked ? 'translate-x-5' : 'translate-x-0'}`} />
    </button>
  )
}

function TagEditor({ tags, onChange }: { tags: string[]; onChange: (t: string[]) => void }) {
  const [input, setInput] = useState('')
  const add = () => { const v = input.trim(); if (v && !tags.includes(v)) { onChange([...tags, v]); setInput('') } }
  return (
    <div className="space-y-2">
      <div className="flex flex-wrap gap-1.5">
        {tags.map((t, i) => (
          <span key={i} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-[var(--accent-cyan)]/10 border border-[var(--accent-cyan)]/20 text-xs text-[var(--accent-cyan)]">
            {t}
            <button onClick={() => onChange(tags.filter((_, j) => j !== i))} className="hover:text-white transition-colors"><X className="h-3 w-3" /></button>
          </span>
        ))}
      </div>
      <div className="flex gap-2">
        <input value={input} onChange={e => setInput(e.target.value)} onKeyDown={e => e.key === 'Enter' && (e.preventDefault(), add())} placeholder="输入后按 Enter" className={inputCls + ' !text-xs'} />
        <button onClick={add} className="px-2 py-1 rounded-lg bg-[var(--accent-cyan)]/10 text-[var(--accent-cyan)] hover:bg-[var(--accent-cyan)]/20 transition-colors text-xs">
          <Plus className="h-3.5 w-3.5" />
        </button>
      </div>
    </div>
  )
}

function SectionCard({ title, subtitle, children }: { title: string; subtitle?: string; children: ReactNode }) {
  return (
    <div className="rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)]/60 overflow-hidden">
      <div className="px-4 py-2.5 border-b border-[var(--border-color)] bg-[var(--bg-tertiary)]/30">
        <div className="flex items-center gap-2">
          <h3 className="text-sm font-medium text-[var(--text-secondary)]">{title}</h3>
          {subtitle && <span className="text-[10px] text-[var(--text-muted)] bg-[var(--bg-tertiary)] px-1.5 py-0.5 rounded">{subtitle}</span>}
        </div>
      </div>
      <div className="p-4 space-y-4">{children}</div>
    </div>
  )
}

interface KnownSource {
  key: string
  label: string
  description: string
}

function SourceEditor({
  sources,
  onChange,
  knownSources,
  examplePaths,
  showCustom = true,
}: {
  sources: string[]
  onChange: (s: string[]) => void
  knownSources: KnownSource[]
  examplePaths?: string[]
  showCustom?: boolean
}) {
  const [customInput, setCustomInput] = useState('')
  const knownKeys = new Set(knownSources.map(k => k.key))
  const customPaths = sources.filter(s => !knownKeys.has(s))

  const toggleKnown = (key: string) => {
    if (sources.includes(key)) {
      onChange(sources.filter(s => s !== key))
    } else {
      onChange([...sources, key])
    }
  }

  const addCustom = () => {
    const v = customInput.trim()
    if (v && !sources.includes(v)) {
      onChange([...sources, v])
      setCustomInput('')
    }
  }

  const removeCustom = (path: string) => {
    onChange(sources.filter(s => s !== path))
  }

  return (
    <div className="space-y-4">
      {/* Known sources as toggles */}
      <div className="space-y-2">
        {knownSources.map(src => (
          <div key={src.key} className="flex items-center justify-between py-1.5">
            <div className="flex-1 min-w-0">
              <div className="text-sm text-[var(--text-primary)]">{src.label}</div>
              <div className="text-xs text-[var(--text-muted)] font-mono">{src.description}</div>
            </div>
            <Toggle checked={sources.includes(src.key)} onChange={() => toggleKnown(src.key)} />
          </div>
        ))}
      </div>

      {/* Custom paths (only shown when showCustom is true) */}
      {showCustom && (
        <div className="space-y-2">
          <div className="text-xs font-medium text-[var(--text-muted)] uppercase tracking-wider">自定义路径</div>
          {customPaths.length > 0 && (
            <div className="flex flex-wrap gap-1.5">
              {customPaths.map((p, i) => (
                <span key={i} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-[var(--accent-cyan)]/10 border border-[var(--accent-cyan)]/20 text-xs text-[var(--accent-cyan)] font-mono">
                  {p}
                  <button onClick={() => removeCustom(p)} className="hover:text-white transition-colors"><X className="h-3 w-3" /></button>
                </span>
              ))}
            </div>
          )}
          <div className="flex gap-2">
            <input
              value={customInput}
              onChange={e => setCustomInput(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && (e.preventDefault(), addCustom())}
              placeholder="输入绝对路径，如 D:\my-skills"
              className={inputCls + ' !text-xs font-mono'}
            />
            <button onClick={addCustom} className="px-2 py-1 rounded-lg bg-[var(--accent-cyan)]/10 text-[var(--accent-cyan)] hover:bg-[var(--accent-cyan)]/20 transition-colors text-xs shrink-0">
              <Plus className="h-3.5 w-3.5" />
            </button>
          </div>
          {examplePaths && (
            <p className="text-xs text-[var(--text-muted)]">
              示例: {examplePaths.join('、')}
            </p>
          )}
        </div>
      )}
    </div>
  )
}

function MapEntryHeader({ name, onDelete, onRename }: { name: string; onDelete: () => void; onRename?: (n: string) => void }) {
  const [editing, setEditing] = useState(false)
  const [val, setVal] = useState(name)
  return (
    <div className="flex items-center gap-2 px-4 py-2 bg-[var(--bg-tertiary)]/50 border-b border-[var(--border-color)]">
      {editing ? (
        <input value={val} onChange={e => setVal(e.target.value)} onBlur={() => { setEditing(false); onRename?.(val.trim() || name) }} onKeyDown={e => e.key === 'Enter' && (setEditing(false), onRename?.(val.trim() || name))} className={inputCls + ' !py-1 !text-xs max-w-[200px]'} autoFocus />
      ) : (
        <span className="text-sm font-mono text-[var(--accent-cyan)] cursor-pointer" onClick={() => onRename && setEditing(true)}>{name}</span>
      )}
      <div className="flex-1" />
      <button onClick={onDelete} className="p-1 rounded text-red-400/60 hover:text-red-400 hover:bg-red-500/10 transition-colors"><Trash2 className="h-3.5 w-3.5" /></button>
    </div>
  )
}

// ── Expert API helpers (reusable, also used by ExpertSelector) ───────
export async function getSelectedExpert(sessionId: string): Promise<{ expert_name: string | null; expert: ExpertItem | null }> {
  const resp = await fetch(`/api/experts/selected?session_id=${encodeURIComponent(sessionId)}`)
  if (!resp.ok) return { expert_name: null, expert: null }
  return resp.json()
}

export async function selectExpert(sessionId: string, expertName: string | null): Promise<{ success: boolean; error?: string }> {
  const resp = await fetch('/api/experts/select', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ session_id: sessionId, expert_name: expertName }),
  })
  const data = await resp.json().catch(() => ({}))
  if (!resp.ok || !data.success) return { success: false, error: data.error || '切换专家失败' }
  return { success: true }
}

// ── Main Component ─────────────────────────────────────
export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPageProps) {
  const [config, setConfig] = useState<AppConfig | null>(null)
  const [activeTab, setActiveTab] = useState<TabId>(initialTab ?? 'providers')
  const [loading, setLoading] = useState(true)
  // Connection settings (localStorage-based)
  const [connHost, setConnHost] = useState(() => {
    try { return localStorage.getItem('picobot-gateway-host') || '127.0.0.1' } catch { return '127.0.0.1' }
  })
  const [connPort, setConnPort] = useState(() => {
    try { const p = parseInt(localStorage.getItem('picobot-gateway-port') || '19876', 10); return isNaN(p) ? 19876 : p } catch { return 19876 }
  })
  const [connError, setConnError] = useState('')

  const [saving, setSaving] = useState(false)
  const [error, setError] = useState('')
  const [toast, setToast] = useState('')
  const [dirty, setDirty] = useState(false)
  const [showRestartDialog, setShowRestartDialog] = useState(false)
  const [restarting, setRestarting] = useState(false)
  const [mcpStatus, setMcpStatus] = useState<McpStatusResponse | null>(null)
  const [skillList, setSkillList] = useState<SkillListResponse | null>(null)
  const [skillListLoading, setSkillListLoading] = useState(false)
  const [subagentList, setSubagentList] = useState<SubagentListResponse | null>(null)
  const [subagentListLoading, setSubagentListLoading] = useState(false)
  const [expertList, setExpertList] = useState<ExpertListResponse | null>(null)
  const [expertListLoading, setExpertListLoading] = useState(false)
  const [editingExpert, setEditingExpert] = useState<{
    mode: 'create' | 'edit'
    name?: string
    scope: string
    nameField: string
    description: string
    body: string
  } | null>(null)
  const [editingExpertError, setEditingExpertError] = useState('')
  const [savingExpert, setSavingExpert] = useState(false)

  const fetchMcpStatus = useCallback(async () => {
    try {
      const resp = await fetch('/api/mcp/status')
      if (resp.ok) setMcpStatus(await resp.json())
    } catch { /* ignore fetch errors */ }
  }, [])

  const fetchSkillList = useCallback(async () => {
    setSkillListLoading(true)
    try {
      const resp = await fetch('/api/skills')
      if (resp.ok) setSkillList(await resp.json())
    } catch { /* ignore fetch errors */ }
    finally { setSkillListLoading(false) }
  }, [])

  const toggleSkill = useCallback(async (name: string, scope: string, enabled: boolean) => {
    const resp = await fetch('/api/skills/toggle', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, scope, enabled }),
    })
    return resp
  }, [])

  const fetchSubagentList = useCallback(async () => {
    setSubagentListLoading(true)
    try {
      const resp = await fetch('/api/subagents')
      if (resp.ok) setSubagentList(await resp.json())
    } catch { /* ignore fetch errors */ }
    finally { setSubagentListLoading(false) }
  }, [])

  const toggleSubagent = useCallback(async (name: string, scope: string, enabled: boolean) => {
    const resp = await fetch('/api/subagents/toggle', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, scope, enabled }),
    })
    return resp
  }, [])

  const fetchExpertList = useCallback(async () => {
    setExpertListLoading(true)
    try {
      const resp = await fetch('/api/experts')
      if (resp.ok) setExpertList(await resp.json())
    } catch { /* ignore fetch errors */ }
    finally { setExpertListLoading(false) }
  }, [])

  const toggleExpert = useCallback(async (name: string, scope: string, enabled: boolean) => {
    const resp = await fetch('/api/experts/toggle', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, scope, enabled }),
    })
    return resp
  }, [])

  const createExpert = useCallback(async (payload: { name: string; description: string; body: string; scope: string }) => {
    const resp = await fetch('/api/experts/create', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    })
    return resp
  }, [])

  const updateExpert = useCallback(async (payload: { name: string; scope: string; description?: string; body?: string }) => {
    const resp = await fetch('/api/experts/update', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    })
    return resp
  }, [])

  const deleteExpert = useCallback(async (name: string, scope: string) => {
    const resp = await fetch(`/api/experts/delete?name=${encodeURIComponent(name)}&scope=${encodeURIComponent(scope)}`, {
      method: 'DELETE',
    })
    return resp
  }, [])

  const handleClose = useCallback(() => {
    if (dirty && !confirm('有未保存的更改，确定要关闭吗？')) return
    onClose()
  }, [dirty, onClose])

  // Load config
  useEffect(() => {
    fetch('/api/config').then(r => r.json()).then(data => {
      setConfig(data)
      setLoading(false)
    }).catch(e => { setError('加载配置失败: ' + e.message); setLoading(false) })
  }, [])

  // Fetch MCP status when MCP tab is selected
  useEffect(() => {
    if (activeTab === 'mcp') fetchMcpStatus()
  }, [activeTab, fetchMcpStatus])

  // Fetch skill list when skills tab is selected
  useEffect(() => {
    if (activeTab === 'skills') fetchSkillList()
  }, [activeTab, fetchSkillList])

  // Fetch subagent list when subagents tab is selected
  useEffect(() => {
    if (activeTab === 'subagents') fetchSubagentList()
  }, [activeTab, fetchSubagentList])

  // Fetch expert list when experts tab is selected
  useEffect(() => {
    if (activeTab === 'experts') fetchExpertList()
  }, [activeTab, fetchExpertList])

  // ESC to close
  useEffect(() => {
    const h = (e: KeyboardEvent) => { if (e.key === 'Escape') handleClose() }
    document.addEventListener('keydown', h)
    return () => document.removeEventListener('keydown', h)
  }, [handleClose])

  const update = useCallback(<K extends keyof AppConfig>(key: K, value: AppConfig[K]) => {
    setConfig(prev => prev ? { ...prev, [key]: value } : prev)
    setDirty(true)
  }, [])

  const handleSave = async () => {
    if (!config) return
    setSaving(true); setError('')
    try {
      const resp = await fetch('/api/config', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ config }),
      })
      const data = await resp.json()
      if (!resp.ok) throw new Error(data.message || data.error || '保存失败')
      // Config is now synced to both disk and in-memory state,
      // so the local state is already correct. No need to re-fetch.
      setDirty(false)
      // Show restart confirmation dialog
      setShowRestartDialog(true)
    } catch (e: any) {
      setError(e.message || '保存失败')
    } finally {
      setSaving(false)
    }
  }

  const handleRestart = async () => {
    setShowRestartDialog(false)
    setRestarting(true)
    try {
      const resp = await fetch('/api/restart', { method: 'POST' })
      const data = await resp.json()
      if (resp.status === 409) {
        setToast(data.message || '有任务运行中，请等待完成后再试')
        setRestarting(false)
        setTimeout(() => setToast(''), 5000)
        return
      }
      if (!resp.ok) throw new Error(data.message || '重启失败')
      setToast('服务正在重启，页面将自动重连...')
      // Poll /health until gateway is back
      const poll = async () => {
        for (let i = 0; i < 30; i++) {
          await new Promise(r => setTimeout(r, 1000))
          try {
            const r = await fetch('/health')
            if (r.ok) {
              const refreshed = await fetch('/api/config').then(r => r.json())
              setConfig(refreshed)
              setToast('服务已重启，配置已生效')
              setRestarting(false)
              setTimeout(() => setToast(''), 3000)
              return
            }
          } catch { /* gateway not ready yet */ }
        }
        setToast('重启超时，请手动刷新页面')
        setRestarting(false)
        setTimeout(() => setToast(''), 5000)
      }
      poll()
    } catch (e: any) {
      setError(e.message || '重启失败')
      setRestarting(false)
    }
  }

  // ── Render sections ──────────────────────────────────
  if (loading) return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
      <Loader2 className="h-8 w-8 text-[var(--accent-cyan)] animate-spin" />
    </div>
  )

  if (!config) return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
      <div className="text-red-400 text-center space-y-3">
        <AlertTriangle className="h-10 w-10 mx-auto" />
        <p>{error || '加载失败'}</p>
        <button onClick={onClose} className="px-4 py-2 rounded-lg bg-[var(--bg-tertiary)] text-sm">关闭</button>
      </div>
    </div>
  )

  const handleSaveConnection = () => {
    const host = connHost.trim()
    if (!host) { setConnError('主机地址不能为空'); return }
    if (connPort < 1 || connPort > 65535) { setConnError('端口号必须在 1-65535 之间'); return }
    setConnError('')
    localStorage.setItem('picobot-gateway-host', host)
    localStorage.setItem('picobot-gateway-port', String(connPort))
    onSaveConnection?.(host, connPort)
    setToast('连接设置已保存，正在重连...')
    setTimeout(() => setToast(''), 3000)
  }

  const renderConnection = () => (
    <div className="space-y-5">
      <SectionCard title="WebSocket 连接">
        <Field label="主机地址"><input value={connHost} onChange={e => { setConnHost(e.target.value); setConnError('') }} className={inputCls} placeholder="127.0.0.1" /></Field>
        <Field label="端口号"><input type="number" value={connPort} onChange={e => { setConnPort(+e.target.value); setConnError('') }} min={1} max={65535} className={inputCls} placeholder="19876" /></Field>
        {connError && <div className="text-sm text-red-400 bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2">{connError}</div>}
        <div className="text-xs text-[var(--text-muted)] bg-[var(--overlay-dim)] rounded-lg px-3 py-2 font-mono">ws://{connHost.trim() || '...'}:{connPort || '...'}/ws</div>
      </SectionCard>
      <button onClick={handleSaveConnection} className="flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-medium text-white bg-[var(--accent-cyan)]/20 border border-[var(--accent-cyan)]/30 hover:bg-[var(--accent-cyan)]/30 transition-all">
        <Wifi className="h-4 w-4" /> 保存并重连
      </button>
    </div>
  )

  const renderGateway = () => (
    <div className="space-y-5">
      <SectionCard title="连接">
        <Field label="主机地址"><input value={config.gateway.host} onChange={e => update('gateway', { ...config.gateway, host: e.target.value })} className={inputCls} /></Field>
        <Field label="端口"><input type="number" value={config.gateway.port} onChange={e => update('gateway', { ...config.gateway, port: +e.target.value })} className={inputCls} /></Field>
      </SectionCard>
      <SectionCard title="行为">
        <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">显示工具结果</span><Toggle checked={config.gateway.show_tool_results} onChange={v => update('gateway', { ...config.gateway, show_tool_results: v })} /></div>
        <Field label="Agent Prompt 重新注入间隔" hint="每多少轮对话重新注入系统提示"><input type="number" value={config.gateway.agent_prompt_reinject_every} onChange={e => update('gateway', { ...config.gateway, agent_prompt_reinject_every: +e.target.value })} className={inputCls} /></Field>
        <Field label="最大并发请求数"><input type="number" value={config.gateway.max_concurrent_requests} onChange={e => update('gateway', { ...config.gateway, max_concurrent_requests: +e.target.value })} className={inputCls} /></Field>
        <Field label="Session TTL (小时)" hint="留空表示不过期"><input type="number" value={config.gateway.session_ttl_hours ?? ''} onChange={e => { const v = e.target.value; update('gateway', { ...config.gateway, session_ttl_hours: v ? +v : undefined }) }} className={inputCls} placeholder="24" /></Field>
      </SectionCard>
    </div>
  )

  const renderProviders = () => {
    const entries = Object.entries(config.providers)
    const addProvider = () => {
      const name = prompt('Provider 名称:')?.trim()
      if (name && !config.providers[name]) {
        update('providers', { ...config.providers, [name]: { type: 'openai', base_url: '', api_key: '', extra_headers: {}, llm_timeout_secs: 120, memory_maintenance_timeout_secs: 600 } })
      }
    }
    const delProvider = (name: string) => { if (confirm(`删除 Provider "${name}"?`)) { const { [name]: _, ...rest } = config.providers; update('providers', rest) } }
    const renameProvider = (oldName: string, newName: string) => {
      if (newName === oldName || !newName) return
      const entries = Object.entries(config.providers)
      const newMap: Record<string, ProviderConfig> = {}
      for (const [k, v] of entries) { newMap[k === oldName ? newName : k] = v }
      update('providers', newMap)
    }
    const updProvider = (name: string, patch: Partial<ProviderConfig>) => {
      update('providers', { ...config.providers, [name]: { ...config.providers[name], ...patch } })
    }
    return (
      <div className="space-y-4">
        {entries.map(([name, p]) => (
          <div key={name} className="rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)]/60 overflow-hidden">
            <MapEntryHeader name={name} onDelete={() => delProvider(name)} onRename={n => renameProvider(name, n)} />
            <div className="p-4 space-y-3">
              <Field label="类型"><select value={p.type} onChange={e => updProvider(name, { type: e.target.value })} className={selectCls}><option value="openai">OpenAI</option><option value="anthropic">Anthropic</option></select></Field>
              <Field label="Base URL"><input value={p.base_url} onChange={e => updProvider(name, { base_url: e.target.value })} className={inputCls} /></Field>
              <Field label="API Key"><input type="password" value={p.api_key} onChange={e => updProvider(name, { api_key: e.target.value })} className={inputCls} /></Field>
              <Field label="LLM 超时 (秒)"><input type="number" value={p.llm_timeout_secs} onChange={e => updProvider(name, { llm_timeout_secs: +e.target.value })} className={inputCls} /></Field>
              <Field label="记忆维护超时 (秒)"><input type="number" value={p.memory_maintenance_timeout_secs} onChange={e => updProvider(name, { memory_maintenance_timeout_secs: +e.target.value })} className={inputCls} /></Field>
            </div>
          </div>
        ))}
        <button onClick={addProvider} className="flex items-center gap-2 px-4 py-2.5 rounded-xl border border-dashed border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:border-[var(--accent-cyan)]/30 transition-colors text-sm w-full justify-center">
          <Plus className="h-4 w-4" /> 添加服务商
        </button>
      </div>
    )
  }

  const renderModels = () => {
    const entries = Object.entries(config.models)
    const addModel = () => {
      const name = prompt('Model 名称:')?.trim()
      if (name && !config.models[name]) update('models', { ...config.models, [name]: { model_id: name } })
    }
    const delModel = (name: string) => { if (confirm(`删除 Model "${name}"?`)) { const { [name]: _, ...rest } = config.models; update('models', rest) } }
    const updModel = (name: string, patch: Partial<ModelConfig>) => update('models', { ...config.models, [name]: { ...config.models[name], ...patch } })
    return (
      <div className="space-y-4">
        {entries.map(([name, m]) => (
          <div key={name} className="rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)]/60 overflow-hidden">
            <MapEntryHeader name={name} onDelete={() => delModel(name)} />
            <div className="p-4 space-y-3">
              <Field label="Model ID"><input value={m.model_id} onChange={e => updModel(name, { model_id: e.target.value })} className={inputCls} /></Field>
              <Field label="Temperature" hint="控制回复随机性，0 表示确定性输出，值越大越随机。留空使用模型默认值"><input type="number" step="0.1" value={m.temperature ?? ''} onChange={e => updModel(name, { temperature: e.target.value ? +e.target.value : undefined })} className={inputCls} placeholder="0.7" /></Field>
              <Field label="Max Tokens" hint="模型单次回复最大生成 token 数，超出会被截断。留空使用模型默认值（如 4096/8192）"><input type="number" value={m.max_tokens ?? ''} onChange={e => updModel(name, { max_tokens: e.target.value ? +e.target.value : undefined })} className={inputCls} placeholder="4096" /></Field>
              <Field label="Context Window Tokens" hint="模型上下文窗口大小，用于内部历史消息压缩/裁剪计算。留空默认 128000"><input type="number" value={m.context_window_tokens ?? ''} onChange={e => updModel(name, { context_window_tokens: e.target.value ? +e.target.value : undefined })} className={inputCls} placeholder="128000" /></Field>
            </div>
          </div>
        ))}
        <button onClick={addModel} className="flex items-center gap-2 px-4 py-2.5 rounded-xl border border-dashed border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:border-[var(--accent-cyan)]/30 transition-colors text-sm w-full justify-center">
          <Plus className="h-4 w-4" /> 添加模型
        </button>
      </div>
    )
  }

  const renderAgents = () => {
    const entries = Object.entries(config.agents)
    const providerNames = Object.keys(config.providers)
    const modelNames = Object.keys(config.models)
    const addAgent = () => {
      const name = prompt('Agent 名称:')?.trim()
      if (name && !config.agents[name]) update('agents', { ...config.agents, [name]: { provider: providerNames[0] || '', model: modelNames[0] || '', max_tool_iterations: 100, tool_result_max_chars: 100000, context_tool_result_trim_chars: 2000 } })
    }
    const delAgent = (name: string) => { if (confirm(`删除 Agent "${name}"?`)) { const { [name]: _, ...rest } = config.agents; update('agents', rest) } }
    const updAgent = (name: string, patch: Partial<AgentConfig>) => update('agents', { ...config.agents, [name]: { ...config.agents[name], ...patch } })
    return (
      <div className="space-y-4">
        {entries.map(([name, a]) => (
          <div key={name} className="rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)]/60 overflow-hidden">
            <MapEntryHeader name={name} onDelete={() => delAgent(name)} />
            <div className="p-4 space-y-3">
              <Field label="Provider"><select value={a.provider} onChange={e => updAgent(name, { provider: e.target.value })} className={selectCls}>{providerNames.map(p => <option key={p} value={p}>{p}</option>)}</select></Field>
              <Field label="Model"><select value={a.model} onChange={e => updAgent(name, { model: e.target.value })} className={selectCls}>{modelNames.map(m => <option key={m} value={m}>{m}</option>)}</select></Field>
              <Field label="最大工具迭代次数"><input type="number" value={a.max_tool_iterations} onChange={e => updAgent(name, { max_tool_iterations: +e.target.value })} className={inputCls} /></Field>
              <Field label="工具结果最大字符数"><input type="number" value={a.tool_result_max_chars} onChange={e => updAgent(name, { tool_result_max_chars: +e.target.value })} className={inputCls} /></Field>
              <Field label="上下文工具结果裁剪字符数"><input type="number" value={a.context_tool_result_trim_chars} onChange={e => updAgent(name, { context_tool_result_trim_chars: +e.target.value })} className={inputCls} /></Field>
            </div>
          </div>
        ))}
        <button onClick={addAgent} className="flex items-center gap-2 px-4 py-2.5 rounded-xl border border-dashed border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:border-[var(--accent-cyan)]/30 transition-colors text-sm w-full justify-center">
          <Plus className="h-4 w-4" /> 添加代理
        </button>
      </div>
    )
  }

  const renderTime = () => (
    <SectionCard title="时区设置">
      <Field label="时区" hint="IANA 格式">
        <select
          value={config.time.timezone}
          onChange={e => update('time', { timezone: e.target.value })}
          className={inputCls}
        >
          {TIMEZONE_OPTIONS.map(tz => (
            <option key={tz.value} value={tz.value}>{tz.label}</option>
          ))}
        </select>
      </Field>
    </SectionCard>
  )

  const renderScheduler = () => (
    <div className="space-y-5">
      <SectionCard title="调度器">
        <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用调度器</span><Toggle checked={config.scheduler.enabled} onChange={v => update('scheduler', { ...config.scheduler, enabled: v })} /></div>
        <Field label="Tick 分辨率 (ms)"><input type="number" value={config.scheduler.tick_resolution_ms} onChange={e => update('scheduler', { ...config.scheduler, tick_resolution_ms: +e.target.value })} className={inputCls} /></Field>
        <Field label="工作队列容量"><input type="number" value={config.scheduler.worker_queue_capacity} onChange={e => update('scheduler', { ...config.scheduler, worker_queue_capacity: +e.target.value })} className={inputCls} /></Field>
        <Field label="Misfire 策略"><select value={config.scheduler.misfire_policy} onChange={e => update('scheduler', { ...config.scheduler, misfire_policy: e.target.value as any })} className={selectCls}><option value="skip">跳过 (Skip)</option><option value="catch_up">追赶 (Catch Up)</option></select></Field>
      </SectionCard>
    </div>
  )

  const SKILL_KNOWN_SOURCES: KnownSource[] = [
    { key: 'user', label: '用户技能', description: '~/.picobot/skills' },
    { key: 'user_agent', label: '用户 Agent 技能', description: '~/.agents/skills' },
    { key: 'user_openclaw', label: '用户 OpenClaw 技能', description: '~/.openclaw/skills' },
    { key: 'project', label: '项目技能', description: '.picobot/skills' },
    { key: 'project_agent', label: '项目 Agent 技能', description: '.agents/skills' },
    { key: 'project_openclaw', label: '项目 OpenClaw 技能', description: '.openclaw/skills' },
  ]

  const SUBAGENT_KNOWN_SOURCES: KnownSource[] = [
    { key: 'user', label: '用户子代理', description: '~/.picobot/subagents' },
    { key: 'project', label: '项目子代理', description: '.picobot/subagents' },
  ]

  const renderSkills = () => (
    <div className="space-y-5">
      <SectionCard title="技能系统">
        <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用技能</span><Toggle checked={config.skills.enabled} onChange={v => update('skills', { ...config.skills, enabled: v })} /></div>
        <Field label="最大索引字符数"><input type="number" value={config.skills.max_index_chars} onChange={e => update('skills', { ...config.skills, max_index_chars: +e.target.value })} className={inputCls} /></Field>
        <Field label="最大展示技能数"><input type="number" value={config.skills.max_listed_skills} onChange={e => update('skills', { ...config.skills, max_listed_skills: +e.target.value })} className={inputCls} /></Field>
      </SectionCard>
      <SectionCard title="来源目录">
        <SourceEditor
          sources={config.skills.sources}
          onChange={v => update('skills', { ...config.skills, sources: v })}
          knownSources={SKILL_KNOWN_SOURCES}
          examplePaths={['D:\\my-skills', '/home/user/shared-skills']}
        />
      </SectionCard>
      {renderDiscoveredSkills()}
    </div>
  )

  const renderDiscoveredSkills = () => {
    if (!skillList || !skillList.skills_system_enabled) return null

    const skills = skillList.skills

    const handleToggle = async (name: string, currentlyEnabled: boolean) => {
      // Optimistic update
      const prevSkillList = skillList
      setSkillList({
        ...skillList,
        skills: skills.map(s =>
          s.name === name
            ? { ...s, disabled_in_scopes: currentlyEnabled ? ['project'] : [] }
            : s
        ),
      })

      try {
        const resp = await toggleSkill(name, 'project', !currentlyEnabled)
        const data = await resp.json()
        if (!resp.ok || !data.success) {
          // Rollback
          setSkillList(prevSkillList)
          setToast(data.error || '切换技能状态失败')
          setTimeout(() => setToast(''), 3000)
          return
        }
        // Update with server response
        setSkillList({
          ...prevSkillList,
          skills: prevSkillList.skills.map(s =>
            s.name === name
              ? { ...s, disabled_in_scopes: data.disabled_in_scopes || [] }
              : s
          ),
        })
      } catch {
        // Rollback
        setSkillList(prevSkillList)
        setToast('网络错误，切换技能状态失败')
        setTimeout(() => setToast(''), 3000)
      }
    }

    return (
      <SectionCard title="已发现技能" subtitle="即时生效">
        {skillListLoading && skills.length === 0 ? (
          <div className="flex items-center gap-2 text-sm text-[var(--text-muted)]">
            <Loader2 className="h-4 w-4 animate-spin" /> 加载中...
          </div>
        ) : skills.length === 0 ? (
          <p className="text-sm text-[var(--text-muted)]">未发现任何技能，请检查来源目录配置</p>
        ) : (
          <div className="space-y-1">
            {skills.map(skill => {
              const isEnabled = skill.disabled_in_scopes.length === 0
              return (
                <div key={skill.name} className="flex items-center gap-3 py-2 px-2 rounded-lg hover:bg-[var(--bg-hover)] transition-colors">
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2">
                      <span className="text-sm font-mono text-[var(--text-primary)]">{skill.name}</span>
                      <span className="text-[10px] px-1.5 py-0.5 rounded bg-[var(--bg-tertiary)] text-[var(--text-muted)] uppercase tracking-wider">{skill.source}</span>
                    </div>
                    <p className="text-xs text-[var(--text-muted)] truncate mt-0.5">{skill.description}</p>
                  </div>
                  <Toggle checked={isEnabled} onChange={() => handleToggle(skill.name, isEnabled)} />
                </div>
              )
            })}
          </div>
        )}
      </SectionCard>
    )
  }

  const TASK_KNOWN_TOOLS: KnownSource[] = [
    { key: 'read', label: 'Read', description: '读取文件' },
    { key: 'edit', label: 'Edit', description: '编辑文件' },
    { key: 'write', label: 'Write', description: '写入文件' },
    { key: 'bash', label: 'Bash', description: '执行 Shell 命令' },
    { key: 'http_request', label: 'HTTP Request', description: '发送 HTTP 请求' },
    { key: 'web_fetch', label: 'Web Fetch', description: '抓取网页内容' },
    { key: 'memory_search', label: 'Memory Search', description: '搜索记忆' },
    { key: 'get_time', label: 'Get Time', description: '获取当前时间' },
    { key: 'calculator', label: 'Calculator', description: '计算器' },
    { key: 'skill_activate', label: 'Skill Activate', description: '激活技能' },
    { key: 'skill_list', label: 'Skill List', description: '列出技能' },
    { key: 'send_session_message', label: 'Send Session Message', description: '发送会话消息' },
  ]

  const renderTools = () => (
    <div className="space-y-5">
      <SectionCard title="禁用工具列表">
        <TagEditor tags={config.tools.disabled} onChange={v => update('tools', { ...config.tools, disabled: v })} />
      </SectionCard>
      <SectionCard title="Task 子代理">
        <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用 Task 工具</span><Toggle checked={config.tools.task.enabled} onChange={v => update('tools', { ...config.tools, task: { ...config.tools.task, enabled: v } })} /></div>
        <Field label="最大执行时间 (秒)"><input type="number" value={config.tools.task.max_execution_secs} onChange={e => update('tools', { ...config.tools, task: { ...config.tools.task, max_execution_secs: +e.target.value } })} className={inputCls} /></Field>
        <Field label="探索模式最大执行时间 (秒)"><input type="number" value={config.tools.task.explore_max_execution_secs} onChange={e => update('tools', { ...config.tools, task: { ...config.tools.task, explore_max_execution_secs: +e.target.value } })} className={inputCls} /></Field>
        <Field label="TTL (小时)"><input type="number" value={config.tools.task.ttl_hours} onChange={e => update('tools', { ...config.tools, task: { ...config.tools.task, ttl_hours: +e.target.value } })} className={inputCls} /></Field>
      </SectionCard>
      <SectionCard title="允许的工具列表">
        <SourceEditor
          sources={config.tools.task.allowed_tools}
          onChange={v => update('tools', { ...config.tools, task: { ...config.tools.task, allowed_tools: v } })}
          knownSources={TASK_KNOWN_TOOLS}
          showCustom={false}
        />
      </SectionCard>
    </div>
  )

  const renderMemory = () => (
    <SectionCard title="记忆维护">
      <Field label="最大合并比例" hint="0.0 - 1.0，单次最多合并/删除的记忆比例"><input type="number" step="0.05" min="0" max="1" value={config.memory_maintenance.max_merge_ratio} onChange={e => update('memory_maintenance', { ...config.memory_maintenance, max_merge_ratio: +e.target.value })} className={inputCls} /></Field>
      <Field label="最小保留记忆数"><input type="number" value={config.memory_maintenance.min_memories_to_keep} onChange={e => update('memory_maintenance', { ...config.memory_maintenance, min_memories_to_keep: +e.target.value })} className={inputCls} /></Field>
      <Field label="单组最大合并数"><input type="number" value={config.memory_maintenance.max_merge_per_group} onChange={e => update('memory_maintenance', { ...config.memory_maintenance, max_merge_per_group: +e.target.value })} className={inputCls} /></Field>
    </SectionCard>
  )

  const renderImage = () => (
    <SectionCard title="图片上下文">
      <Field label="上下文中最大图片数" hint="发送给模型的图片数量上限"><input type="number" value={config.image_context.max_images_in_context} onChange={e => update('image_context', { ...config.image_context, max_images_in_context: +e.target.value })} className={inputCls} /></Field>
      <Field label="图片最大存活轮次" hint="超过此轮次后不再提交给模型"><input type="number" value={config.image_context.max_image_age_rounds} onChange={e => update('image_context', { ...config.image_context, max_image_age_rounds: +e.target.value })} className={inputCls} /></Field>
    </SectionCard>
  )

  const renderSubagents = () => (
    <div className="space-y-5">
      <SectionCard title="子代理">
        <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用子代理发现</span><Toggle checked={config.subagents.enabled} onChange={v => update('subagents', { ...config.subagents, enabled: v })} /></div>
      </SectionCard>
      <SectionCard title="来源目录">
        <SourceEditor
          sources={config.subagents.sources}
          onChange={v => update('subagents', { ...config.subagents, sources: v })}
          knownSources={SUBAGENT_KNOWN_SOURCES}
          examplePaths={['D:\\my-subagents', '/home/user/shared-agents']}
        />
      </SectionCard>
      {renderDiscoveredSubagents()}
    </div>
  )

  const renderDiscoveredSubagents = () => {
    if (!subagentList || !subagentList.subagents_system_enabled) return null

    const subagents = subagentList.subagents

    const handleToggle = async (name: string, currentlyEnabled: boolean) => {
      const prevList = subagentList
      setSubagentList({
        ...subagentList,
        subagents: subagents.map(s =>
          s.name === name
            ? { ...s, disabled_in_scopes: currentlyEnabled ? ['project'] : [] }
            : s
        ),
      })

      try {
        const resp = await toggleSubagent(name, 'project', !currentlyEnabled)
        const data = await resp.json()
        if (!resp.ok || !data.success) {
          setSubagentList(prevList)
          setToast(data.error || '切换子代理状态失败')
          setTimeout(() => setToast(''), 3000)
          return
        }
        setSubagentList({
          ...prevList,
          subagents: prevList.subagents.map(s =>
            s.name === name
              ? { ...s, disabled_in_scopes: data.disabled_in_scopes || [] }
              : s
          ),
        })
      } catch {
        setSubagentList(prevList)
        setToast('网络错误，切换子代理状态失败')
        setTimeout(() => setToast(''), 3000)
      }
    }

    return (
      <SectionCard title="已发现子代理" subtitle="即时生效">
        {subagentListLoading && subagents.length === 0 ? (
          <div className="flex items-center gap-2 text-sm text-[var(--text-muted)]">
            <Loader2 className="h-4 w-4 animate-spin" /> 加载中...
          </div>
        ) : subagents.length === 0 ? (
          <p className="text-sm text-[var(--text-muted)]">未发现任何子代理</p>
        ) : (
          <div className="space-y-1">
            {subagents.map(subagent => {
              const isEnabled = subagent.disabled_in_scopes.length === 0
              return (
                <div key={subagent.name} className="flex items-center gap-3 py-2 px-2 rounded-lg hover:bg-[var(--bg-hover)] transition-colors">
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2">
                      <span className="text-sm font-mono text-[var(--text-primary)]">{subagent.name}</span>
                      <span className="text-[10px] px-1.5 py-0.5 rounded bg-[var(--bg-tertiary)] text-[var(--text-muted)] uppercase tracking-wider">{subagent.source}</span>
                    </div>
                    <p className="text-xs text-[var(--text-muted)] truncate mt-0.5">{subagent.description}</p>
                  </div>
                  <Toggle checked={isEnabled} onChange={() => handleToggle(subagent.name, isEnabled)} />
                </div>
              )
            })}
          </div>
        )}
      </SectionCard>
    )
  }

  const EXPERT_KNOWN_SOURCES: KnownSource[] = [
    { key: 'user', label: '用户专家', description: '~/.picobot/experts' },
    { key: 'project', label: '项目专家', description: '.picobot/experts' },
  ]

  const renderExperts = () => (
    <div className="space-y-5">
      <SectionCard title="专家系统">
        <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用专家系统</span><Toggle checked={config.experts.enabled} onChange={v => update('experts', { ...config.experts, enabled: v })} /></div>
      </SectionCard>
      <SectionCard title="来源目录">
        <SourceEditor
          sources={config.experts.sources}
          onChange={v => update('experts', { ...config.experts, sources: v })}
          knownSources={EXPERT_KNOWN_SOURCES}
          examplePaths={['D:\\my-experts', '/home/user/shared-experts']}
        />
      </SectionCard>
      {renderDiscoveredExperts()}
      <button
        onClick={() => {
          setEditingExpertError('')
          setEditingExpert({ mode: 'create', scope: 'project', nameField: '', description: '', body: '' })
        }}
        className="flex items-center gap-2 px-4 py-2.5 rounded-xl border border-dashed border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:border-[var(--accent-cyan)]/30 transition-colors text-sm w-full justify-center"
      >
        <Plus className="h-4 w-4" /> 添加专家
      </button>
      {renderExpertModal()}
    </div>
  )

  const renderDiscoveredExperts = () => {
    if (!expertList || !expertList.experts_system_enabled) return null

    const experts = expertList.experts

    const handleToggle = async (name: string, currentlyEnabled: boolean) => {
      const prevList = expertList
      setExpertList({
        ...expertList,
        experts: experts.map(e =>
          e.name === name
            ? { ...e, disabled_in_scopes: currentlyEnabled ? ['project'] : [] }
            : e
        ),
      })

      try {
        const resp = await toggleExpert(name, 'project', !currentlyEnabled)
        const data = await resp.json()
        if (!resp.ok || !data.success) {
          setExpertList(prevList)
          setToast(data.error || '切换专家状态失败')
          setTimeout(() => setToast(''), 3000)
          return
        }
        setExpertList({
          ...prevList,
          experts: prevList.experts.map(e =>
            e.name === name
              ? { ...e, disabled_in_scopes: data.disabled_in_scopes || [] }
              : e
          ),
        })
      } catch {
        setExpertList(prevList)
        setToast('网络错误，切换专家状态失败')
        setTimeout(() => setToast(''), 3000)
      }
    }

    const handleEdit = (expert: ExpertItem) => {
      setEditingExpertError('')
      setEditingExpert({
        mode: 'edit',
        name: expert.name,
        scope: 'project',
        nameField: expert.name,
        description: expert.description,
        body: expert.body ?? '',
      })
    }

    const handleDelete = async (name: string) => {
      if (!confirm(`确定删除专家 "${name}" 吗？此操作将删除对应文件。`)) return
      try {
        const resp = await deleteExpert(name, 'project')
        const data = await resp.json()
        if (!resp.ok || !data.success) {
          setToast(data.error || '删除专家失败')
          setTimeout(() => setToast(''), 3000)
          return
        }
        setToast('专家已删除')
        setTimeout(() => setToast(''), 3000)
        fetchExpertList()
      } catch {
        setToast('网络错误，删除专家失败')
        setTimeout(() => setToast(''), 3000)
      }
    }

    return (
      <SectionCard title="已发现专家" subtitle="即时生效">
        {expertListLoading && experts.length === 0 ? (
          <div className="flex items-center gap-2 text-sm text-[var(--text-muted)]">
            <Loader2 className="h-4 w-4 animate-spin" /> 加载中...
          </div>
        ) : experts.length === 0 ? (
          <p className="text-sm text-[var(--text-muted)]">未发现任何专家</p>
        ) : (
          <div className="space-y-1">
            {experts.map(expert => {
              const isEnabled = expert.disabled_in_scopes.length === 0
              return (
                <div key={expert.name} className="flex items-center gap-3 py-2 px-2 rounded-lg hover:bg-[var(--bg-hover)] transition-colors">
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2">
                      <span className="text-sm font-mono text-[var(--text-primary)]">{expert.name}</span>
                      <span className="text-[10px] px-1.5 py-0.5 rounded bg-[var(--bg-tertiary)] text-[var(--text-muted)] uppercase tracking-wider">{expert.source}</span>
                    </div>
                    <p className="text-xs text-[var(--text-muted)] truncate mt-0.5">{expert.description}</p>
                  </div>
                  <button
                    onClick={() => handleEdit(expert)}
                    className="p-1 rounded text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:bg-[var(--overlay-hover)] transition-colors"
                    title="编辑"
                  >
                    <Pencil className="h-3.5 w-3.5" />
                  </button>
                  <button
                    onClick={() => handleDelete(expert.name)}
                    className="p-1 rounded text-[var(--text-muted)] hover:text-red-400 hover:bg-red-500/10 transition-colors"
                    title="删除"
                  >
                    <Trash2 className="h-3.5 w-3.5" />
                  </button>
                  <Toggle checked={isEnabled} onChange={() => handleToggle(expert.name, isEnabled)} />
                </div>
              )
            })}
          </div>
        )}
      </SectionCard>
    )
  }

  const renderExpertModal = () => {
    if (!editingExpert) return null
    const isEdit = editingExpert.mode === 'edit'
    const canSave = editingExpert.nameField.trim() && editingExpert.description.trim()

    const handleSave = async () => {
      if (!canSave) return
      setSavingExpert(true)
      setEditingExpertError('')
      try {
        const resp = isEdit
          ? await updateExpert({
              name: editingExpert.nameField,
              scope: 'project',
              description: editingExpert.description,
              body: editingExpert.body,
            })
          : await createExpert({
              name: editingExpert.nameField,
              description: editingExpert.description,
              body: editingExpert.body,
              scope: 'project',
            })
        const data = await resp.json().catch(() => ({}))
        if (!resp.ok) {
          setEditingExpertError(data.error || data.message || '保存失败')
          setSavingExpert(false)
          return
        }
        setToast(isEdit ? '专家已更新' : '专家已创建')
        setTimeout(() => setToast(''), 3000)
        setEditingExpert(null)
        fetchExpertList()
      } catch (e: any) {
        setEditingExpertError(e.message || '网络错误')
      } finally {
        setSavingExpert(false)
      }
    }

    return (
      <div className="absolute inset-0 z-20 flex items-center justify-center bg-black/50 backdrop-blur-sm rounded-2xl">
        <div className="bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-xl p-6 w-[90%] max-w-lg mx-4 shadow-2xl animate-[scaleIn_0.2s_ease-out]">
          <div className="flex items-center gap-2 mb-4">
            <UserCheck className="h-5 w-5 text-[var(--accent-cyan)]" />
            <h3 className="text-sm font-semibold text-[var(--text-primary)]">
              {isEdit ? '编辑专家' : '添加专家'}
            </h3>
          </div>
          <div className="space-y-3">
            <Field label="名称">
              <input
                value={editingExpert.nameField}
                onChange={e => setEditingExpert(prev => prev ? { ...prev, nameField: e.target.value } : prev)}
                disabled={isEdit}
                placeholder="如 translator"
                className={inputCls + (isEdit ? ' opacity-60 cursor-not-allowed' : '')}
                autoFocus={!isEdit}
              />
            </Field>
            <Field label="描述" hint="必填，简要说明专家身份">
              <input
                value={editingExpert.description}
                onChange={e => setEditingExpert(prev => prev ? { ...prev, description: e.target.value } : prev)}
                placeholder="如 翻译专家"
                className={inputCls}
              />
            </Field>
            <Field label="专家提示词正文" hint="markdown 格式，将作为系统提示词注入">
              <textarea
                value={editingExpert.body}
                onChange={e => setEditingExpert(prev => prev ? { ...prev, body: e.target.value } : prev)}
                placeholder="你是一名专业翻译..."
                className={inputCls + ' min-h-[200px] resize-y font-mono text-xs'}
              />
            </Field>
            {editingExpertError && (
              <div className="text-sm text-red-400 bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2">
                {editingExpertError}
              </div>
            )}
          </div>
          <div className="flex gap-3 justify-end mt-5">
            <button
              onClick={() => setEditingExpert(null)}
              className="px-4 py-2 rounded-lg text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--overlay-hover)] transition-colors"
            >
              取消
            </button>
            <button
              onClick={handleSave}
              disabled={!canSave || savingExpert}
              className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium text-white bg-[var(--accent-cyan)]/20 border border-[var(--accent-cyan)]/30 hover:bg-[var(--accent-cyan)]/30 hover:border-[var(--accent-cyan)]/50 transition-all disabled:opacity-40 disabled:cursor-not-allowed"
            >
              {savingExpert ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
              {savingExpert ? '保存中...' : '保存'}
            </button>
          </div>
        </div>
      </div>
    )
  }

  const renderMcp = () => {
    const entries = Object.entries(config.mcpServers)
    const statusFor = (key: string) => mcpStatus?.servers?.find(s => s.key === key)
    const addMcp = () => {
      const name = prompt('MCP 服务器名称:')?.trim()
      if (name && !config.mcpServers[name]) {
        update('mcpServers', { ...config.mcpServers, [name]: { type: 'stdio', is_active: true, command: '', args: [] } })
      }
    }
    const delMcp = (name: string) => { if (confirm(`删除 MCP 服务器 "${name}"?`)) { const { [name]: _, ...rest } = config.mcpServers; update('mcpServers', rest) } }
    const updMcp = (name: string, patch: Partial<McpServerConfig>) => update('mcpServers', { ...config.mcpServers, [name]: { ...config.mcpServers[name], ...patch } })
    return (
      <div className="space-y-4">
        {/* MCP Status Summary */}
        {mcpStatus && mcpStatus.enabled && (
          <div className="flex items-center gap-3 p-3 rounded-lg bg-[var(--bg-tertiary)] text-xs">
            <div className="flex items-center gap-1.5">
              <span className={`inline-block w-2 h-2 rounded-full ${mcpStatus.connected_servers > 0 ? 'bg-green-400' : 'bg-gray-400'}`} />
              <span className="text-[var(--text-secondary)]">{mcpStatus.connected_servers}/{mcpStatus.total_servers} 已连接</span>
            </div>
            {mcpStatus.failed_servers > 0 && (
              <span className="text-red-400">{mcpStatus.failed_servers} 失败</span>
            )}
            <span className="text-[var(--text-muted)]">{mcpStatus.total_tools} 个工具</span>
            <button onClick={fetchMcpStatus} className="ml-auto px-2 py-1 rounded text-[var(--text-muted)] hover:text-[var(--accent-cyan)] transition-colors" title="刷新状态">
              <RefreshCw className="h-3 w-3" />
            </button>
          </div>
        )}
        {entries.map(([name, s]) => {
          const st = statusFor(name)
          return (
          <div key={name} className="rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)]/60 overflow-hidden">
            <div className="flex items-center gap-2 p-3 border-b border-[var(--border-color)]">
              {st ? (
                st.connected
                  ? <span className="inline-flex items-center gap-1 text-xs text-green-400"><span className="w-2 h-2 rounded-full bg-green-400" /> {st.tool_count} 工具</span>
                  : st.error
                    ? <span className="inline-flex items-center gap-1 text-xs text-red-400" title={st.error}><span className="w-2 h-2 rounded-full bg-red-400" /> 错误</span>
                    : <span className="inline-flex items-center gap-1 text-xs text-gray-400"><span className="w-2 h-2 rounded-full bg-gray-400" /> 未连接</span>
              ) : null}
              <span className="flex-1 text-sm font-medium text-[var(--text-primary)]">{name}</span>
              <button onClick={() => delMcp(name)} className="p-1 rounded text-[var(--text-muted)] hover:text-red-400 transition-colors"><Trash2 className="h-3.5 w-3.5" /></button>
            </div>
            <div className="p-4 space-y-3">
              <Field label="传输类型">
                <select value={s.type} onChange={e => updMcp(name, { type: e.target.value as McpServerConfig['type'] })} className={selectCls}>
                  <option value="stdio">stdio (本地命令)</option>
                  <option value="streamableHttp">streamableHttp (HTTP)</option>
                </select>
              </Field>
              <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用</span><Toggle checked={s.is_active} onChange={v => updMcp(name, { is_active: v })} /></div>
              <Field label="描述"><input value={s.description ?? ''} onChange={e => updMcp(name, { description: e.target.value || undefined })} className={inputCls} placeholder="可选描述" /></Field>
              {s.type === 'stdio' && (
                <>
                  <Field label="命令" hint="如 npx, node, cargo, uv"><input value={s.command ?? ''} onChange={e => updMcp(name, { command: e.target.value })} className={inputCls} placeholder="npx" /></Field>
                  <Field label="参数" hint="空格分隔"><input value={(s.args ?? []).join(' ')} onChange={e => updMcp(name, { args: e.target.value ? e.target.value.split(/\s+/) : [] })} className={inputCls} placeholder="-y @modelcontextprotocol/server-filesystem /tmp" /></Field>
                  <Field label="工作目录 (cwd)" hint="可选。子进程运行目录，常用于 uv/python 项目解析 pyproject.toml 或 venv"><input value={s.cwd ?? ''} onChange={e => updMcp(name, { cwd: e.target.value || undefined })} className={inputCls} placeholder="E:\code_project\my-mcp-server" /></Field>
                  <Field label="环境变量" hint="KEY=VALUE，每行一个">
                    <textarea
                      value={Object.entries(s.env ?? {}).map(([k, v]) => `${k}=${v}`).join('\n')}
                      onChange={e => {
                        const lines = e.target.value.split('\n').filter(l => l.includes('='))
                        const env: Record<string, string> = {}
                        lines.forEach(l => { const [k, ...rest] = l.split('='); if (k) env[k.trim()] = rest.join('=').trim() })
                        updMcp(name, { env: Object.keys(env).length > 0 ? env : undefined })
                      }}
                      className={inputCls + ' min-h-[60px] resize-y font-mono text-xs'}
                      placeholder="API_KEY=xxx"
                    />
                  </Field>
                </>
              )}
              {(s.type === 'streamableHttp' || s.type === 'http') && (
                <>
                  <Field label="Base URL"><input value={s.base_url ?? ''} onChange={e => updMcp(name, { base_url: e.target.value })} className={inputCls} placeholder="http://localhost:3000/mcp" /></Field>
                  <Field label="请求头" hint="KEY=VALUE，每行一个，支持 ${ENV_VAR}">
                    <textarea
                      value={Object.entries(s.headers ?? {}).map(([k, v]) => `${k}=${v}`).join('\n')}
                      onChange={e => {
                        const lines = e.target.value.split('\n').filter(l => l.includes('='))
                        const headers: Record<string, string> = {}
                        lines.forEach(l => { const [k, ...rest] = l.split('='); if (k) headers[k.trim()] = rest.join('=').trim() })
                        updMcp(name, { headers: Object.keys(headers).length > 0 ? headers : undefined })
                      }}
                      className={inputCls + ' min-h-[60px] resize-y font-mono text-xs'}
                      placeholder="Authorization=Bearer ${TOKEN}"
                    />
                  </Field>
                </>
              )}
            </div>
          </div>
          )
        })}
        <button onClick={addMcp} className="flex items-center gap-2 px-4 py-2.5 rounded-xl border border-dashed border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:border-[var(--accent-cyan)]/30 transition-colors text-sm w-full justify-center">
          <Plus className="h-4 w-4" /> 添加 MCP 服务器
        </button>
      </div>
    )
  }

  const renderChannels = () => {
    const entries = Object.entries(config.channels)
    const addChannel = () => {
      const name = prompt('渠道名称:')?.trim()
      if (name && !config.channels[name]) {
        update('channels', { ...config.channels, [name]: { type: 'feishu', enabled: false, app_id: '', app_secret: '' } })
      }
    }
    const delChannel = (name: string) => { if (confirm(`删除渠道 "${name}"?`)) { const { [name]: _, ...rest } = config.channels; update('channels', rest) } }
    const updChannel = (name: string, patch: Record<string, any>) => update('channels', { ...config.channels, [name]: { ...config.channels[name], ...patch } })
    const getChannelType = (ch: any): string => {
      if (ch.type) return ch.type
      if (ch.app_id !== undefined || ch.app_secret !== undefined) return 'feishu'
      if (ch.cred_path !== undefined) return 'wechat'
      return 'feishu'
    }
    return (
      <div className="space-y-4">
        {entries.map(([name, ch]) => {
          const chType = getChannelType(ch)
          return (
            <div key={name} className="rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)]/60 overflow-hidden">
              <MapEntryHeader name={name} onDelete={() => delChannel(name)} />
              <div className="p-4 space-y-3">
                <Field label="渠道类型">
                  <select value={chType} onChange={e => updChannel(name, { type: e.target.value })} className={selectCls}>
                    <option value="feishu">飞书 (Feishu)</option>
                    <option value="wechat">微信 (WeChat)</option>
                  </select>
                </Field>
                <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用</span><Toggle checked={!!ch.enabled} onChange={v => updChannel(name, { enabled: v })} /></div>
                {chType === 'feishu' && (
                  <>
                    <Field label="App ID"><input value={ch.app_id ?? ''} onChange={e => updChannel(name, { app_id: e.target.value })} className={inputCls} /></Field>
                    <Field label="App Secret"><input type="password" value={ch.app_secret ?? ''} onChange={e => updChannel(name, { app_secret: e.target.value })} className={inputCls} /></Field>
                    <Field label="绑定 Agent" hint="留空使用 default">
                  <select value={ch.agent ?? ''} onChange={e => updChannel(name, { agent: e.target.value })} className={selectCls}>
                    <option value="">default</option>
                    {Object.keys(config.agents).map(a => <option key={a} value={a}>{a}</option>)}
                  </select>
                </Field>
                    <Field label="最大消息字符数"><input type="number" value={ch.max_message_chars ?? 20000} onChange={e => updChannel(name, { max_message_chars: +e.target.value })} className={inputCls} /></Field>
                    <Field label="回复上下文最大字符数"><input type="number" value={ch.reply_context_max_chars ?? 20000} onChange={e => updChannel(name, { reply_context_max_chars: +e.target.value })} className={inputCls} /></Field>
                  </>
                )}
                {chType === 'wechat' && (
                  <>
                    <Field label="凭证文件路径"><input value={ch.cred_path ?? ''} onChange={e => updChannel(name, { cred_path: e.target.value })} className={inputCls} placeholder="~/.picobot/wechat/credentials.json" /></Field>
                    <Field label="Base URL"><input value={ch.base_url ?? 'https://ilinkai.weixin.qq.com'} onChange={e => updChannel(name, { base_url: e.target.value })} className={inputCls} /></Field>
                    <Field label="绑定 Agent" hint="留空使用 default">
                  <select value={ch.agent ?? ''} onChange={e => updChannel(name, { agent: e.target.value })} className={selectCls}>
                    <option value="">default</option>
                    {Object.keys(config.agents).map(a => <option key={a} value={a}>{a}</option>)}
                  </select>
                </Field>
                    <div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">强制重新登录</span><Toggle checked={!!ch.force_login} onChange={v => updChannel(name, { force_login: v })} /></div>
                  </>
                )}
              </div>
            </div>
          )
        })}
        <button onClick={addChannel} className="flex items-center gap-2 px-4 py-2.5 rounded-xl border border-dashed border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:border-[var(--accent-cyan)]/30 transition-colors text-sm w-full justify-center">
          <Plus className="h-4 w-4" /> 添加渠道
        </button>
      </div>
    )
  }

  const renderContent = () => {
    switch (activeTab) {
      case 'connection': return renderConnection()
      case 'gateway': return renderGateway()
      case 'providers': return renderProviders()
      case 'models': return renderModels()
      case 'agents': return renderAgents()
      case 'time': return renderTime()
      case 'scheduler': return renderScheduler()
      case 'skills': return renderSkills()
      case 'tools': return renderTools()
      case 'memory': return renderMemory()
      case 'image': return renderImage()
      case 'subagents': return renderSubagents()
      case 'experts': return renderExperts()
      case 'mcp': return renderMcp()
      case 'channels': return renderChannels()
    }
  }

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm animate-[fadeIn_0.15s_ease-out]">
      <div
        className="relative flex flex-col w-[92vw] max-w-4xl h-[85vh] rounded-2xl border border-[var(--border-color)] bg-[var(--bg-primary)] shadow-2xl overflow-hidden animate-[scaleIn_0.2s_ease-out]"
        onClick={e => e.stopPropagation()}
      >
        {/* Header */}
        <div className="shrink-0 flex items-center gap-3 px-6 py-4 border-b border-[var(--border-color)] bg-[var(--bg-secondary)]/80 backdrop-blur-md">
          <Settings className="h-5 w-5 text-[var(--accent-cyan)]" />
          <span className="text-lg font-semibold text-[var(--text-primary)]">系统配置</span>
          {dirty && <span className="text-xs text-amber-400 bg-amber-500/10 px-2 py-0.5 rounded-full">未保存</span>}
          <div className="flex-1" />
          <button onClick={handleClose} className="p-2 rounded-lg text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--overlay-hover)] transition-colors" title="关闭 (Esc)">
            <X className="h-5 w-5" />
          </button>
        </div>

        {/* Body */}
        <div className="flex flex-1 min-h-0">
          {/* Left sidebar tabs */}
          <div className="shrink-0 w-48 border-r border-[var(--border-color)] bg-[var(--bg-secondary)]/40 overflow-y-auto py-2">
            {TABS.map(tab => {
              const Icon = tab.icon
              const active = activeTab === tab.id
              return (
                <button
                  key={tab.id}
                  onClick={() => setActiveTab(tab.id)}
                  className={`w-full flex items-center gap-2.5 px-4 py-2.5 text-sm transition-all relative ${
                    active
                      ? 'text-[var(--accent-cyan)] bg-[var(--accent-cyan)]/5'
                      : 'text-[var(--text-muted)] hover:text-[var(--text-secondary)] hover:bg-[var(--overlay-hover)]'
                  }`}
                >
                  {active && <span className="absolute left-0 top-1 bottom-1 w-[2px] rounded-r bg-[var(--accent-cyan)]" />}
                  <Icon className="h-4 w-4 shrink-0" />
                  <span>{tab.label}</span>
                </button>
              )
            })}
          </div>

          {/* Right content */}
          <div className="flex-1 overflow-y-auto p-6">
            {renderContent()}
          </div>
        </div>

        {/* Footer */}
        <div className="shrink-0 px-6 py-3 border-t border-[var(--border-color)] bg-[var(--bg-secondary)]/80 backdrop-blur-md flex items-center gap-3">
          {error && <span className="text-sm text-red-400 truncate max-w-xs">{error}</span>}
          <div className="flex-1" />
          <button onClick={handleClose} className="px-4 py-2 rounded-lg text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--overlay-hover)] transition-colors">
            取消
          </button>
          <button
            onClick={handleSave}
            disabled={saving || !dirty}
            className="flex items-center gap-2 px-5 py-2 rounded-lg text-sm font-medium text-white bg-[var(--accent-cyan)]/20 border border-[var(--accent-cyan)]/30 hover:bg-[var(--accent-cyan)]/30 hover:border-[var(--accent-cyan)]/50 transition-all disabled:opacity-40 disabled:cursor-not-allowed"
          >
            {saving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
            {saving ? '保存中...' : '保存配置'}
          </button>
        </div>

        {/* Toast */}
        {toast && (
          <div className="absolute top-20 left-1/2 -translate-x-1/2 z-10 flex items-center gap-2 px-5 py-3 rounded-xl bg-emerald-500/15 border border-emerald-500/30 text-emerald-400 text-sm shadow-lg backdrop-blur-md animate-[fadeIn_0.2s_ease-out]">
            {restarting ? <Loader2 className="h-4 w-4 animate-spin" /> : <CheckCircle className="h-4 w-4" />}
            {toast}
          </div>
        )}

        {/* Restart confirmation dialog */}
        {showRestartDialog && (
          <div className="absolute inset-0 z-20 flex items-center justify-center bg-black/50 backdrop-blur-sm rounded-2xl">
            <div className="bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-xl p-6 max-w-sm mx-4 shadow-2xl animate-[scaleIn_0.2s_ease-out]">
              <div className="flex items-center gap-3 mb-4">
                <div className="flex items-center justify-center w-10 h-10 rounded-full bg-[var(--accent-cyan)]/10">
                  <RefreshCw className="h-5 w-5 text-[var(--accent-cyan)]" />
                </div>
                <div>
                  <h3 className="text-sm font-semibold text-[var(--text-primary)]">配置已保存</h3>
                  <p className="text-xs text-[var(--text-muted)]">是否立即重启服务使配置生效？</p>
                </div>
              </div>
              <div className="flex gap-3 justify-end">
                <button
                  onClick={() => setShowRestartDialog(false)}
                  className="px-4 py-2 rounded-lg text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--overlay-hover)] transition-colors"
                >
                  稍后
                </button>
                <button
                  onClick={handleRestart}
                  className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium text-white bg-[var(--accent-cyan)]/20 border border-[var(--accent-cyan)]/30 hover:bg-[var(--accent-cyan)]/30 hover:border-[var(--accent-cyan)]/50 transition-all"
                >
                  <RefreshCw className="h-4 w-4" />
                  立即重启
                </button>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  )
}
