import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { apiFetch } from '@/lib/tauri'

// ── Existing types ──

type FileCategory = 'config' | 'instructions' | 'plugins' | 'history' | 'debug' | 'cache' | 'sessions' | 'backups' | 'other'

interface AgentFile {
  name: string
  path: string
  type: 'json' | 'markdown' | 'text' | 'jsonl'
  category: FileCategory
  size: number
}

interface Agent {
  id: string
  name: string
  directory: string
  files: AgentFile[]
}

interface OpenFile {
  path: string
  name: string
  type: string
  content: string
  originalContent: string
  agentId: string
  category: FileCategory
}

export interface CategoryInfo {
  key: FileCategory
  label: string
  icon: string
  color: string
  files: AgentFile[]
}

// ── New types for config management ──

type PermissionStatus = 'allow' | 'deny' | 'ask'
type AgentTab = 'files' | 'tools' | 'mcps' | 'plugins' | 'hooks' | 'skills'
type HookEventType = 'UserPromptSubmit' | 'PreToolUse' | 'PostToolUse' | 'SessionStart' | 'Stop' | 'Notification' | 'PermissionRequest'

interface ParsedPermission {
  raw: string
  tool: string
  params: string | null
  category: 'base' | 'mcp'
  server?: string
  host?: string
}

interface ToolEntry {
  name: string
  fullKey: string
  category: 'base' | 'mcp'
  server?: string
  host?: string
  status: PermissionStatus
  rules: string[]
}

interface McpServerEntry {
  name: string
  type: string
  command?: string
  args?: string[]
  url?: string
  env?: Record<string, string>
  enabled: boolean
}

interface HookCommand {
  type: string
  command: string
  timeout?: number
}

interface HookEntry {
  matcher: string | null
  hooks: HookCommand[]
}

interface SkillEntry {
  name: string
  description: string
  path: string
  skillMdContent: string
  references: { name: string; path: string }[]
}

interface PluginEntry {
  name: string
  description: string
  author: string
  mcpConfig: any
  installed: boolean
}

// ── Constants ──

const HOOK_EVENT_TYPES: HookEventType[] = [
  'UserPromptSubmit', 'PreToolUse', 'PostToolUse',
  'SessionStart', 'Stop', 'Notification', 'PermissionRequest'
]

const CATEGORY_META: Record<FileCategory, { label: string; icon: string; color: string; order: number }> = {
  config:       { label: 'Configuration',   icon: 'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z|M12 12m-3 0a3 3 0 1 0 6 0a3 3 0 1 0-6 0', color: '#6366f1', order: 0 },
  instructions: { label: 'Instructions',    icon: 'M4 19.5A2.5 2.5 0 0 1 6.5 17H20|M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z', color: '#3b82f6', order: 1 },
  plugins:      { label: 'Plugins',         icon: 'M12 2L2 7l10 5 10-5-10-5z|M2 17l10 5 10-5|M2 12l10 5 10-5', color: '#8b5cf6', order: 2 },
  history:      { label: 'History',         icon: 'M12 8v4l3 3|M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z', color: '#f59e0b', order: 3 },
  debug:        { label: 'Debug logs',      icon: 'M12 12m-1 0a1 1 0 1 0 2 0 1 1 0 0 0-2 0|M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z', color: '#ef4444', order: 4 },
  cache:        { label: 'Cache',           icon: 'M22 12H2|M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z', color: '#06b6d4', order: 5 },
  sessions:     { label: 'Session data',    icon: 'M4 17l6-6 4 4 6-6|M4 17h16', color: '#10b981', order: 6 },
  backups:      { label: 'Backups',         icon: 'M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8|M21 3v5h-5', color: '#9ca3af', order: 7 },
  other:        { label: 'Other files',     icon: 'M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z|M14 2v6h6', color: '#78716c', order: 8 }
}

export { CATEGORY_META, HOOK_EVENT_TYPES }
export type { FileCategory, AgentFile, Agent, AgentTab, HookEventType, ToolEntry, McpServerEntry, HookEntry, HookCommand, SkillEntry, PluginEntry, ParsedPermission, PermissionStatus }

function formatSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}

export { formatSize }

export const useAgentsStore = defineStore('agents', () => {
  // ── Existing state ──
  const agents = ref<Agent[]>([])
  const selectedAgentId = ref<string | null>(null)
  const openFile = ref<OpenFile | null>(null)
  const loading = ref(false)
  const saving = ref(false)
  const error = ref<string | null>(null)
  const collapsedCategories = ref<Set<string>>(new Set())

  // ── New state: tab ──
  const activeTab = ref<AgentTab>('files')

  // ── New state: tools ──
  const toolEntries = ref<ToolEntry[]>([])
  const toolsFilter = ref('')
  const toolsLoading = ref(false)

  // ── New state: MCP servers ──
  const mcpServers = ref<McpServerEntry[]>([])
  const mcpsLoading = ref(false)
  const enableAllProjectMcpServers = ref(false)
  const enabledMcpjsonServers = ref<string[]>([])

  // ── New state: hooks ──
  const hooksConfig = ref<Record<string, HookEntry[]>>({})
  const hooksLoading = ref(false)
  const expandedHookTypes = ref<Set<string>>(new Set())

  // ── New state: skills ──
  const skills = ref<SkillEntry[]>([])
  const skillsLoading = ref(false)
  const selectedSkill = ref<SkillEntry | null>(null)

  // ── New state: plugins ──
  const plugins = ref<PluginEntry[]>([])
  const pluginsLoading = ref(false)

  // ── New state: config metadata ──
  const configFile = ref('')
  const configDirty = ref(false)

  // ── Existing computed ──
  const isDirty = computed(() => {
    if (!openFile.value) return false
    return openFile.value.content !== openFile.value.originalContent
  })

  const selectedAgent = computed(() => {
    if (!selectedAgentId.value) return null
    return agents.value.find(a => a.id === selectedAgentId.value) || null
  })

  const groupedFiles = computed((): CategoryInfo[] => {
    const agent = selectedAgent.value
    if (!agent) return []

    const groups = new Map<FileCategory, AgentFile[]>()
    for (const file of agent.files) {
      const cat = file.category
      if (!groups.has(cat)) groups.set(cat, [])
      groups.get(cat)!.push(file)
    }

    return Array.from(groups.entries())
      .map(([key, files]) => ({
        key,
        label: CATEGORY_META[key].label,
        icon: CATEGORY_META[key].icon,
        color: CATEGORY_META[key].color,
        files
      }))
      .sort((a, b) => CATEGORY_META[a.key].order - CATEGORY_META[b.key].order)
  })

  // ── New computed ──
  const filteredTools = computed(() => {
    if (!toolsFilter.value) return toolEntries.value
    const q = toolsFilter.value.toLowerCase()
    return toolEntries.value.filter(t =>
      t.name.toLowerCase().includes(q) ||
      t.fullKey.toLowerCase().includes(q) ||
      (t.server && t.server.toLowerCase().includes(q))
    )
  })

  const toolsByCategory = computed(() => {
    const base: ToolEntry[] = []
    const mcpGroups: Record<string, ToolEntry[]> = {}

    for (const tool of filteredTools.value) {
      if (tool.category === 'base') {
        base.push(tool)
      } else {
        const key = tool.server || 'unknown'
        if (!mcpGroups[key]) mcpGroups[key] = []
        mcpGroups[key].push(tool)
      }
    }

    return { base, mcpGroups }
  })

  // ── Existing actions ──
  async function fetchAgents() {
    loading.value = true
    error.value = null
    try {
      const res = await apiFetch('/api/agents')
      if (!res.ok) throw new Error('Failed to fetch agents')
      agents.value = await res.json()
      if (agents.value.length && !selectedAgentId.value) {
        selectedAgentId.value = agents.value[0].id
      }
    } catch (e: any) {
      error.value = e.message
    } finally {
      loading.value = false
    }
  }

  function selectAgent(id: string) {
    selectedAgentId.value = id
    openFile.value = null
    // Reload config for new agent when on config tabs
    if (activeTab.value !== 'files') {
      loadTabData()
    }
  }

  async function loadFile(agentId: string, file: AgentFile) {
    error.value = null
    try {
      const res = await apiFetch(`/api/agents/file?path=${encodeURIComponent(file.path)}`)
      if (!res.ok) {
        const data = await res.json()
        throw new Error(data.error || 'Failed to load file')
      }
      const data = await res.json()
      openFile.value = {
        path: file.path,
        name: file.name,
        type: file.type,
        content: data.content,
        originalContent: data.content,
        agentId,
        category: file.category
      }
    } catch (e: any) {
      error.value = e.message
    }
  }

  async function saveFile() {
    if (!openFile.value || !isDirty.value) return
    saving.value = true
    error.value = null
    try {
      const res = await apiFetch('/api/agents/file', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          path: openFile.value.path,
          content: openFile.value.content
        })
      })
      if (!res.ok) {
        const data = await res.json()
        throw new Error(data.error || 'Failed to save file')
      }
      openFile.value.originalContent = openFile.value.content
    } catch (e: any) {
      error.value = e.message
    } finally {
      saving.value = false
    }
  }

  function revertFile() {
    if (!openFile.value) return
    openFile.value.content = openFile.value.originalContent
  }

  function toggleCategory(key: string) {
    if (collapsedCategories.value.has(key)) {
      collapsedCategories.value.delete(key)
    } else {
      collapsedCategories.value.add(key)
    }
  }

  // ── New actions: tab data loading ──

  async function loadTabData() {
    const tab = activeTab.value
    if (tab === 'tools') await fetchConfig()
    else if (tab === 'mcps') await fetchMcpJson()
    else if (tab === 'hooks') await fetchConfig()
    else if (tab === 'skills') await fetchSkills()
    else if (tab === 'plugins') await fetchPlugins()
  }

  async function fetchConfig() {
    toolsLoading.value = true
    hooksLoading.value = true
    error.value = null
    try {
      const agentId = selectedAgentId.value || 'main'
      const [configRes, knownRes] = await Promise.all([
        apiFetch(`/api/agents/config?agentId=${encodeURIComponent(agentId)}`),
        apiFetch('/api/agents/known-tools')
      ])

      if (!configRes.ok) throw new Error('Failed to fetch config')
      if (!knownRes.ok) throw new Error('Failed to fetch known tools')

      const config = await configRes.json()
      const known = await knownRes.json()

      configFile.value = config.configFile
      enableAllProjectMcpServers.value = config.enableAllProjectMcpServers
      enabledMcpjsonServers.value = config.enabledMcpjsonServers

      // Build tool entries from known tools + permissions
      buildToolEntries(config, known)

      // Build hooks config
      hooksConfig.value = config.hooks || {}
    } catch (e: any) {
      error.value = e.message
    } finally {
      toolsLoading.value = false
      hooksLoading.value = false
    }
  }

  function buildToolEntries(config: any, known: any) {
    const entries: ToolEntry[] = []
    const allowMap = new Map<string, ParsedPermission>()
    const denyMap = new Map<string, ParsedPermission>()

    // Index permissions by tool name
    for (const p of config.permissions.allow) {
      allowMap.set(p.raw, p)
    }
    for (const p of config.permissions.deny) {
      denyMap.set(p.raw, p)
    }

    // Base tools
    for (const toolName of known.baseTools) {
      // Find all allow/deny entries for this base tool
      const allowRules: string[] = []
      const denyRules: string[] = []
      let hasSimpleAllow = false
      let hasSimpleDeny = false

      for (const [raw, p] of allowMap) {
        if (p.tool === toolName) {
          if (p.params) allowRules.push(p.params)
          else hasSimpleAllow = true
        }
      }
      for (const [raw, p] of denyMap) {
        if (p.tool === toolName) {
          if (p.params) denyRules.push(p.params)
          else hasSimpleDeny = true
        }
      }

      let status: PermissionStatus = 'ask'
      if (hasSimpleAllow || allowRules.length > 0) status = 'allow'
      if (hasSimpleDeny || denyRules.length > 0) status = 'deny'
      // If both exist, allow takes precedence (list first)
      if (hasSimpleAllow) status = 'allow'

      entries.push({
        name: toolName,
        fullKey: toolName,
        category: 'base',
        status,
        rules: allowRules.length ? allowRules : denyRules
      })
    }

    // MCP tools from known discovery
    for (const [server, hosts] of Object.entries(known.mcpTools as Record<string, Record<string, string[]>>)) {
      for (const [host, tools] of Object.entries(hosts)) {
        for (const toolName of tools) {
          const fullKey = `mcp__${server.replace(/-/g, '_')}__${host}-${toolName}`
          const isAllowed = allowMap.has(fullKey)
          const isDenied = denyMap.has(fullKey)

          entries.push({
            name: toolName,
            fullKey,
            category: 'mcp',
            server: server.replace(/_/g, '-'),
            host,
            status: isAllowed ? 'allow' : isDenied ? 'deny' : 'ask',
            rules: []
          })
        }
      }
    }

    // Also add any permission entries that weren't in known tools
    const allKnownKeys = new Set(entries.map(e => e.fullKey))
    for (const [raw, p] of allowMap) {
      if (!allKnownKeys.has(raw) && !p.params) {
        entries.push({
          name: p.tool,
          fullKey: raw,
          category: p.category,
          server: p.server,
          host: p.host,
          status: 'allow',
          rules: []
        })
      }
    }
    for (const [raw, p] of denyMap) {
      if (!allKnownKeys.has(raw) && !p.params) {
        entries.push({
          name: p.tool,
          fullKey: raw,
          category: p.category,
          server: p.server,
          host: p.host,
          status: 'deny',
          rules: []
        })
      }
    }

    toolEntries.value = entries
    configDirty.value = false
  }

  // ── Tool actions ──

  function setToolStatus(toolName: string, status: PermissionStatus) {
    const tool = toolEntries.value.find(t => t.fullKey === toolName || t.name === toolName)
    if (tool) {
      tool.status = status
      if (status === 'ask') tool.rules = []
      configDirty.value = true
    }
  }

  function cycleToolStatus(toolName: string) {
    const tool = toolEntries.value.find(t => t.fullKey === toolName || t.name === toolName)
    if (!tool) return
    const cycle: PermissionStatus[] = ['ask', 'allow', 'deny']
    const idx = cycle.indexOf(tool.status)
    tool.status = cycle[(idx + 1) % 3]
    if (tool.status === 'ask') tool.rules = []
    configDirty.value = true
  }

  function addToolRule(toolName: string, rule: string) {
    const tool = toolEntries.value.find(t => t.fullKey === toolName || t.name === toolName)
    if (tool && !tool.rules.includes(rule)) {
      tool.rules.push(rule)
      configDirty.value = true
    }
  }

  function removeToolRule(toolName: string, rule: string) {
    const tool = toolEntries.value.find(t => t.fullKey === toolName || t.name === toolName)
    if (tool) {
      tool.rules = tool.rules.filter(r => r !== rule)
      configDirty.value = true
    }
  }

  async function savePermissions() {
    saving.value = true
    error.value = null
    try {
      const allow: string[] = []
      const deny: string[] = []

      for (const tool of toolEntries.value) {
        if (tool.status === 'allow') {
          if (tool.rules.length > 0) {
            for (const rule of tool.rules) {
              allow.push(`${tool.name}(${rule})`)
            }
          } else {
            allow.push(tool.category === 'mcp' ? tool.fullKey : tool.name)
          }
        } else if (tool.status === 'deny') {
          if (tool.rules.length > 0) {
            for (const rule of tool.rules) {
              deny.push(`${tool.name}(${rule})`)
            }
          } else {
            deny.push(tool.category === 'mcp' ? tool.fullKey : tool.name)
          }
        }
      }

      const res = await apiFetch('/api/agents/config/permissions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          agentId: selectedAgentId.value || 'main',
          permissions: { allow, deny }
        })
      })

      if (!res.ok) throw new Error('Failed to save permissions')
      configDirty.value = false
    } catch (e: any) {
      error.value = e.message
    } finally {
      saving.value = false
    }
  }

  // ── MCP actions ──

  async function fetchMcpJson() {
    mcpsLoading.value = true
    error.value = null
    try {
      const [mcpRes, configRes] = await Promise.all([
        apiFetch('/api/agents/mcp-json'),
        apiFetch(`/api/agents/config?agentId=${encodeURIComponent(selectedAgentId.value || 'main')}`)
      ])
      if (!mcpRes.ok) throw new Error('Failed to fetch MCP config')

      const mcpData = await mcpRes.json()
      const configData = configRes.ok ? await configRes.json() : {}

      enableAllProjectMcpServers.value = configData.enableAllProjectMcpServers ?? false
      enabledMcpjsonServers.value = configData.enabledMcpjsonServers || []

      const servers: McpServerEntry[] = []
      const mcpServersObj = mcpData.mcpServers || {}
      for (const [name, config] of Object.entries(mcpServersObj as Record<string, any>)) {
        servers.push({
          name,
          type: config.type || 'stdio',
          command: config.command,
          args: config.args,
          url: config.url,
          env: config.env,
          enabled: enableAllProjectMcpServers.value || enabledMcpjsonServers.value.includes(name)
        })
      }
      mcpServers.value = servers
    } catch (e: any) {
      error.value = e.message
    } finally {
      mcpsLoading.value = false
    }
  }

  async function saveMcpServers() {
    saving.value = true
    error.value = null
    try {
      const mcpServersObj: Record<string, any> = {}
      for (const s of mcpServers.value) {
        const entry: any = { type: s.type }
        if (s.command) entry.command = s.command
        if (s.args?.length) entry.args = s.args
        if (s.url) entry.url = s.url
        if (s.env && Object.keys(s.env).length) entry.env = s.env
        mcpServersObj[s.name] = entry
      }

      const res = await apiFetch('/api/agents/config/mcp', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ mcpServers: mcpServersObj })
      })
      if (!res.ok) throw new Error('Failed to save MCP config')
    } catch (e: any) {
      error.value = e.message
    } finally {
      saving.value = false
    }
  }

  function addMcpServer(server: McpServerEntry) {
    mcpServers.value.push(server)
  }

  function removeMcpServer(name: string) {
    mcpServers.value = mcpServers.value.filter(s => s.name !== name)
  }

  function toggleMcpServer(name: string) {
    const server = mcpServers.value.find(s => s.name === name)
    if (server) server.enabled = !server.enabled
  }

  // ── Hooks actions ──

  async function saveHooks() {
    saving.value = true
    error.value = null
    try {
      const res = await apiFetch('/api/agents/config/hooks', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          agentId: selectedAgentId.value || 'main',
          hooks: hooksConfig.value
        })
      })
      if (!res.ok) throw new Error('Failed to save hooks')
    } catch (e: any) {
      error.value = e.message
    } finally {
      saving.value = false
    }
  }

  function addHook(eventType: string) {
    if (!hooksConfig.value[eventType]) {
      hooksConfig.value[eventType] = []
    }
    hooksConfig.value[eventType].push({
      matcher: null,
      hooks: [{ type: 'command', command: '', timeout: 5000 }]
    })
  }

  function removeHook(eventType: string, index: number) {
    if (hooksConfig.value[eventType]) {
      hooksConfig.value[eventType].splice(index, 1)
      if (hooksConfig.value[eventType].length === 0) {
        delete hooksConfig.value[eventType]
      }
    }
  }

  function updateHook(eventType: string, index: number, entry: HookEntry) {
    if (hooksConfig.value[eventType]?.[index]) {
      hooksConfig.value[eventType][index] = entry
    }
  }

  function toggleHookType(eventType: string) {
    if (expandedHookTypes.value.has(eventType)) {
      expandedHookTypes.value.delete(eventType)
    } else {
      expandedHookTypes.value.add(eventType)
    }
  }

  // ── Skills actions ──

  async function fetchSkills() {
    skillsLoading.value = true
    error.value = null
    try {
      const agentId = selectedAgentId.value || 'main'
      const res = await apiFetch(`/api/agents/skills?agentId=${encodeURIComponent(agentId)}`)
      if (!res.ok) throw new Error('Failed to fetch skills')
      skills.value = await res.json()
      if (skills.value.length && !selectedSkill.value) {
        selectedSkill.value = skills.value[0]
      }
    } catch (e: any) {
      error.value = e.message
    } finally {
      skillsLoading.value = false
    }
  }

  // ── Plugins actions ──

  async function fetchPlugins() {
    pluginsLoading.value = true
    error.value = null
    try {
      const res = await apiFetch('/api/agents/plugins')
      if (!res.ok) throw new Error('Failed to fetch plugins')
      plugins.value = await res.json()
    } catch (e: any) {
      error.value = e.message
    } finally {
      pluginsLoading.value = false
    }
  }

  return {
    // Existing
    agents,
    selectedAgentId,
    selectedAgent,
    openFile,
    loading,
    saving,
    error,
    collapsedCategories,
    isDirty,
    groupedFiles,
    fetchAgents,
    selectAgent,
    loadFile,
    saveFile,
    revertFile,
    toggleCategory,

    // New: tab
    activeTab,

    // New: tools
    toolEntries,
    toolsFilter,
    toolsLoading,
    filteredTools,
    toolsByCategory,
    setToolStatus,
    cycleToolStatus,
    addToolRule,
    removeToolRule,
    savePermissions,

    // New: MCP
    mcpServers,
    mcpsLoading,
    enableAllProjectMcpServers,
    enabledMcpjsonServers,
    fetchMcpJson,
    saveMcpServers,
    addMcpServer,
    removeMcpServer,
    toggleMcpServer,

    // New: hooks
    hooksConfig,
    hooksLoading,
    expandedHookTypes,
    saveHooks,
    addHook,
    removeHook,
    updateHook,
    toggleHookType,

    // New: skills
    skills,
    skillsLoading,
    selectedSkill,
    fetchSkills,

    // New: plugins
    plugins,
    pluginsLoading,
    fetchPlugins,

    // New: config metadata
    configFile,
    configDirty,
    fetchConfig,
    loadTabData
  }
})
