# useInvoker Hook - Guia Completo

O `useInvoker` é um hook que retorna **diretamente a função de execução** de um comando, eliminando a necessidade de chamar `.invoke()`. É a forma mais simples e direta de executar comandos no Commander.

## 📋 Índice

- [Visão Geral](#visão-geral)
- [Hook Principal](#hook-principal---useinvoker)
- [Hooks Especializados](#hooks-especializados)
  - [useAction](#1-useaction---para-comandos-sem-parâmetros)
  - [useCommandState](#2-usecommandstate---com-estado-completo)
  - [useSafeInvoker](#3-usesafeinvoker---execução-segura)
  - [useBoundInvoker](#4-useboundinvoker---input-pré-configurado)
  - [useToggleInvoker](#5-usetoggleinvoker---comandos-toggle)
  - [useBatchInvoker](#6-usebatchinvoker---execução-sequencial)
  - [useParallelInvoker](#7-useparallelinvoker---execução-paralela)
- [Comparação com useCommand](#comparação-com-usecommand)
- [Exemplos Práticos](#exemplos-práticos)
- [Casos de Uso Avançados](#casos-de-uso-avançados)

## 🎯 Visão Geral

O `useInvoker` e sua família de hooks especializados fornecem acesso direto e simplificado aos comandos:

```tsx
// useCommand - precisa chamar .invoke()
const command = useCommand('file:save')
await command.invoke(data)

// useInvoker - executa diretamente!
const save = useInvoker('file:save')
await save(data)  // 🎯 Direto ao ponto!

// Família de hooks especializados
const logout = useAction('user:logout')        // Sem parâmetros
await logout()

const safeCall = useSafeInvoker('api:risky')  // Nunca lança erro
const result = await safeCall()

const saveUser = useBoundInvoker('user:save', { // Input pré-configurado
  createdBy: currentUser.id 
})
await saveUser({ name: 'João' })

const toggle = useToggleInvoker('ui:dark-mode') // Estados booleanos
await toggle(true)

const batch = useBatchInvoker([                 // Execução sequencial
  'validate', 'process', 'save'
])
await batch(inputs)

const parallel = useParallelInvoker([           // Execução paralela
  'api:users', 'api:stats', 'api:logs'
])
const results = await parallel()
```

## 🔧 Hook Principal - useInvoker

### Assinatura

```tsx
useInvoker<TInput, TOutput>(
  key: CommandKey,
  options?: UseInvokerOptions<TInput, TOutput>
): (input?: TInput) => Promise<TOutput>

// SEMPRE retorna uma função de execução direta
```

### Configurações Disponíveis

```tsx
interface UseInvokerOptions<TInput, TOutput> {
  // Execução
  source?: CommandSource          // 'api' | 'palette' | 'shortcut'
  throwOnError?: boolean          // Default: true
  
  // Input
  defaultInput?: Partial<TInput>  // Input padrão mesclado
  
  // Callbacks
  onSuccess?: (result: TOutput) => void
  onError?: (error: Error) => void
}
```

> **Nota**: Para funcionalidades avançadas como retry, debounce, throttle, validação ou transformação, use `useCommand` diretamente.

### Exemplos Básicos

```tsx
// 1. Execução simples
function LogoutButton() {
  const logout = useInvoker('user:logout')
  
  return (
    <button onClick={() => logout()}>
      Sair
    </button>
  )
}

// 2. Com input
function SaveButton({ document }) {
  const saveDoc = useInvoker<SaveInput, SaveResult>('document:save')
  
  const handleSave = async () => {
    const result = await saveDoc({
      id: document.id,
      content: document.content
    })
    toast.success(`Salvo: ${result.filename}`)
  }
  
  return <button onClick={handleSave}>Salvar</button>
}

// 3. Com configurações
function SmartUploader() {
  const upload = useInvoker('file:upload', {
    throwOnError: false,
    defaultInput: { quality: 'high' },
    
    onSuccess: (result) => {
      analytics.track('file_uploaded', { size: result.size })
    },
    
    onError: (error) => {
      notification.error('Upload falhou: ' + error.message)
    }
  })
  
  const handleUpload = async (file: File) => {
    const result = await upload({ file })
    if (result) {
      setUploadedFiles(prev => [...prev, result])
    }
  }
}

// 4. Quando precisar de estado, use useCommand
function FileUploader() {
  const uploader = useCommand('file:upload', {
    onSuccess: (result) => toast.success('Upload concluído!')
  })
  
  return (
    <div>
      <input 
        type="file"
        onChange={(e) => uploader.invoke({ file: e.target.files[0] })}
        disabled={uploader.isLoading}
      />
      
      {uploader.isLoading && <Spinner />}
      {uploader.lastError && (
        <div className="error">{uploader.lastError.message}</div>
      )}
    </div>
  )
}
```

## 🎪 Hooks Especializados

### 1. useAction - Para Comandos Sem Parâmetros

Otimizado para comandos que não recebem input (ou sempre usam o mesmo input).

```tsx
useAction(key: CommandKey): () => Promise<TOutput>
```

#### Exemplos

```tsx
// Ações simples
function NavigationButtons() {
  const goHome = useAction('navigation:home')
  const openSettings = useAction('modal:settings')
  const refreshData = useAction('data:refresh')
  
  return (
    <div className="flex gap-2">
      <button onClick={goHome}>🏠 Home</button>
      <button onClick={openSettings}>⚙️ Configurações</button>
      <button onClick={refreshData}>🔄 Atualizar</button>
    </div>
  )
}

// Comandos do sistema
function SystemActions() {
  const clearCache = useAction('system:clear-cache')
  const exportLogs = useAction('system:export-logs')
  const runHealthCheck = useAction('system:health-check')
  
  const handleClearCache = async () => {
    if (confirm('Limpar cache?')) {
      await clearCache()
      toast.success('Cache limpo!')
    }
  }
  
  return (
    <div className="admin-panel">
      <button onClick={handleClearCache}>Limpar Cache</button>
      <button onClick={exportLogs}>Exportar Logs</button>
      <button onClick={runHealthCheck}>Health Check</button>
    </div>
  )
}

// Ações de usuário
function UserActions() {
  const logout = useAction('user:logout')
  const deleteAccount = useAction('user:delete-account')
  
  const handleDeleteAccount = async () => {
    const confirmed = await showConfirmDialog({
      title: 'Deletar Conta',
      message: 'Esta ação é irreversível!',
      confirmText: 'Deletar',
      cancelText: 'Cancelar'
    })
    
    if (confirmed) {
      await deleteAccount()
      // Usuário será redirecionado automaticamente
    }
  }
  
  return (
    <div className="user-menu">
      <button onClick={logout}>Sair</button>
      <button onClick={handleDeleteAccount} className="danger">
        Deletar Conta
      </button>
    </div>
  )
}
```

### 2. useCommandState - Com Estado Completo

Simplesmente um alias para `useCommand`, mantido para compatibilidade.

```tsx
useCommandState<TInput, TOutput>(
  key: CommandKey,
  options?: UseInvokerOptions
): CommandInvoker<TInput, TOutput>
```

> **Nota**: Com a refatoração, `useCommandState` é funcionalmente idêntico a `useCommand`. Considere usar `useCommand` diretamente.

#### Exemplos

```tsx
// Monitor de operações
function OperationMonitor() {
  const backup = useCommandState('system:backup')
  const sync = useCommandState('data:sync')
  const cleanup = useCommandState('system:cleanup')
  
  const operations = [
    { name: 'Backup', command: backup },
    { name: 'Sincronização', command: sync },
    { name: 'Limpeza', command: cleanup }
  ]
  
  return (
    <div className="operations-panel">
      <h3>Status das Operações</h3>
      {operations.map(({ name, command }) => (
        <div key={name} className="operation-row">
          <span>{name}</span>
          
          {command.isLoading && <Spinner />}
          
          {command.lastExecution && (
            <span className="last-run">
              Última execução: {formatDistanceToNow(command.lastExecution)}
            </span>
          )}
          
          {command.lastError && (
            <span className="error">❌ {command.lastError.message}</span>
          )}
          
          <button 
            onClick={() => command.invoke()}
            disabled={command.isLoading}
          >
            Executar
          </button>
        </div>
      ))}
    </div>
  )
}

// Dashboard com métricas
function SystemDashboard() {
  const healthCheck = useCommandState('system:health-check')
  const getMetrics = useCommandState('system:metrics')
  
  // Auto-refresh a cada 30 segundos
  useInterval(() => {
    if (!healthCheck.isLoading) {
      healthCheck.invoke()
    }
    if (!getMetrics.isLoading) {
      getMetrics.invoke()
    }
  }, 30000)
  
  return (
    <div className="dashboard">
      <div className="health-card">
        <h4>System Health</h4>
        
        {healthCheck.isLoading && <Spinner />}
        
        {healthCheck.lastResult && (
          <div className={`status ${healthCheck.lastResult.status}`}>
            {healthCheck.lastResult.status.toUpperCase()}
          </div>
        )}
        
        <div className="stats">
          <span>Checks realizados: {healthCheck.executionCount}</span>
        </div>
      </div>
      
      <div className="metrics-card">
        <h4>Métricas</h4>
        
        {getMetrics.lastResult && (
          <div className="metrics">
            <div>CPU: {getMetrics.lastResult.cpu}%</div>
            <div>Memória: {getMetrics.lastResult.memory}%</div>
            <div>Disco: {getMetrics.lastResult.disk}%</div>
          </div>
        )}
      </div>
    </div>
  )
}
```

### 3. useSafeInvoker - Execução Segura

Nunca lança erros, sempre retorna `ExecutionResult`.

```tsx
useSafeInvoker<TInput, TOutput>(
  key: CommandKey,
  options?: Omit<UseInvokerOptions, 'throwOnError'>
): (input?: TInput) => Promise<ExecutionResult<TOutput>>

interface ExecutionResult<T> {
  success: boolean
  result?: T
  error?: Error
  command: CommandKey
}
```

#### Exemplos

```tsx
// API calls não-críticas
function OptionalFeatures() {
  const loadRecommendations = useSafeInvoker('ai:recommendations')
  const loadAnalytics = useSafeInvoker('analytics:load')
  
  const [recommendations, setRecommendations] = useState(null)
  const [analytics, setAnalytics] = useState(null)
  
  useEffect(() => {
    // Carrega recursos opcionais sem quebrar a UI
    Promise.all([
      loadRecommendations({ userId: currentUser.id }),
      loadAnalytics({ period: '7d' })
    ]).then(([recResult, analyticsResult]) => {
      if (recResult.success) {
        setRecommendations(recResult.result)
      }
      
      if (analyticsResult.success) {
        setAnalytics(analyticsResult.result)
      }
    })
  }, [])
  
  return (
    <div className="optional-features">
      {recommendations && (
        <RecommendationsWidget data={recommendations} />
      )}
      
      {analytics && (
        <AnalyticsWidget data={analytics} />
      )}
    </div>
  )
}

// Operações em batch com falhas toleradas
function BatchProcessor() {
  const processItem = useSafeInvoker('item:process')
  
  const [results, setResults] = useState([])
  const [isProcessing, setIsProcessing] = useState(false)
  
  const processBatch = async (items) => {
    setIsProcessing(true)
    const results = []
    
    for (const item of items) {
      const result = await processItem({ item })
      results.push({
        item,
        success: result.success,
        data: result.result,
        error: result.error?.message
      })
    }
    
    setResults(results)
    setIsProcessing(false)
    
    const successCount = results.filter(r => r.success).length
    toast.success(`${successCount}/${items.length} itens processados`)
  }
  
  return (
    <div className="batch-processor">
      <button 
        onClick={() => processBatch(selectedItems)}
        disabled={isProcessing}
      >
        {isProcessing ? 'Processando...' : 'Processar Lote'}
      </button>
      
      {results.length > 0 && (
        <div className="results">
          {results.map((result, index) => (
            <div key={index} className={`result ${result.success ? 'success' : 'error'}`}>
              <span>{result.item.name}</span>
              {result.success ? (
                <span className="success">✅ Sucesso</span>
              ) : (
                <span className="error">❌ {result.error}</span>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

// Fallbacks e degradação graciosa
function DataVisualization() {
  const loadLiveData = useSafeInvoker('data:live')
  const loadCachedData = useSafeInvoker('data:cached')
  
  const [data, setData] = useState(null)
  const [dataSource, setDataSource] = useState('loading')
  
  useEffect(() => {
    const loadData = async () => {
      // Tenta dados ao vivo primeiro
      const liveResult = await loadLiveData()
      
      if (liveResult.success) {
        setData(liveResult.result)
        setDataSource('live')
        return
      }
      
      // Fallback para dados em cache
      const cachedResult = await loadCachedData()
      
      if (cachedResult.success) {
        setData(cachedResult.result)
        setDataSource('cached')
      } else {
        setDataSource('unavailable')
      }
    }
    
    loadData()
  }, [])
  
  if (dataSource === 'loading') {
    return <div>Carregando dados...</div>
  }
  
  if (dataSource === 'unavailable') {
    return <div>Dados indisponíveis no momento</div>
  }
  
  return (
    <div className="data-viz">
      {dataSource === 'cached' && (
        <div className="warning">
          ⚠️ Exibindo dados em cache (dados ao vivo indisponíveis)
        </div>
      )}
      
      <DataChart data={data} />
    </div>
  )
}
```

### 4. useBoundInvoker - Input Pré-configurado

Combina input padrão com input fornecido na execução.

```tsx
useBoundInvoker<TInput, TOutput>(
  key: CommandKey,
  boundInput: Partial<TInput>,
  options?: Omit<UseInvokerOptions, 'defaultInput'>
): (input?: Partial<TInput>) => Promise<TOutput>
```

#### Exemplos

```tsx
// Usuário atual em contexto
function CurrentUserActions() {
  const currentUser = useCurrentUser()
  
  // Input sempre inclui userId atual
  const updateProfile = useBoundInvoker('user:update', {
    userId: currentUser.id
  })
  
  const changePassword = useBoundInvoker('user:change-password', {
    userId: currentUser.id
  })
  
  const deleteAccount = useBoundInvoker('user:delete', {
    userId: currentUser.id
  })
  
  const handleUpdateProfile = async (profileData) => {
    // userId é adicionado automaticamente
    await updateProfile({
      name: profileData.name,
      email: profileData.email
      // userId já está incluído
    })
  }
  
  const handleChangePassword = async (passwords) => {
    await changePassword({
      currentPassword: passwords.current,
      newPassword: passwords.new
      // userId já está incluído
    })
  }
}

// Contexto de workspace
function WorkspaceCommands() {
  const { currentWorkspace } = useWorkspace()
  
  // Comandos sempre executam no workspace atual
  const createProject = useBoundInvoker('project:create', {
    workspaceId: currentWorkspace.id,
    createdBy: getCurrentUser().id
  })
  
  const inviteUser = useBoundInvoker('workspace:invite', {
    workspaceId: currentWorkspace.id,
    invitedBy: getCurrentUser().id
  })
  
  const updateSettings = useBoundInvoker('workspace:update-settings', {
    workspaceId: currentWorkspace.id
  })
  
  return (
    <div className="workspace-actions">
      <button onClick={() => createProject({ 
        name: 'Novo Projeto',
        template: 'web-app'
      })}>
        Criar Projeto
      </button>
      
      <button onClick={() => inviteUser({
        email: 'user@example.com',
        role: 'member'
      })}>
        Convidar Usuário
      </button>
    </div>
  )
}

// Configurações de API
function ApiCommands() {
  const apiConfig = useApiConfig()
  
  // Todas as chamadas incluem configuração padrão
  const apiCall = useBoundInvoker('api:call', {
    baseUrl: apiConfig.baseUrl,
    timeout: apiConfig.timeout,
    retries: 3,
    headers: {
      'Authorization': `Bearer ${apiConfig.token}`,
      'User-Agent': 'MyApp/1.0'
    }
  })
  
  const uploadFile = useBoundInvoker('api:upload', {
    baseUrl: apiConfig.baseUrl,
    headers: {
      'Authorization': `Bearer ${apiConfig.token}`
    },
    timeout: 60000  // Upload tem timeout maior
  })
  
  // Uso simplificado
  const fetchUsers = () => apiCall({
    endpoint: '/users',
    method: 'GET'
  })
  
  const createUser = (userData) => apiCall({
    endpoint: '/users',
    method: 'POST',
    body: userData
  })
  
  const uploadAvatar = (file) => uploadFile({
    endpoint: '/avatars',
    file: file
  })
}

// Formulários com dados parciais
function FormCommands() {
  const { formData } = useFormContext()
  
  // Salvamento sempre inclui dados base do formulário
  const saveStep = useBoundInvoker('form:save-step', {
    formId: formData.id,
    userId: formData.userId,
    sessionId: formData.sessionId
  })
  
  const validateStep = useBoundInvoker('form:validate-step', {
    formId: formData.id,
    formType: formData.type
  })
  
  // Uso nos componentes de step
  const saveStep1 = (stepData) => saveStep({
    step: 1,
    data: stepData
  })
  
  const saveStep2 = (stepData) => saveStep({
    step: 2,
    data: stepData
  })
  
  const validateCurrentStep = (stepNumber, data) => validateStep({
    step: stepNumber,
    data: data
  })
}
```

### 5. useToggleInvoker - Comandos Toggle

Especializado para comandos que trabalham com estados booleanos.

```tsx
useToggleInvoker(
  key: CommandKey,
  options?: Omit<UseInvokerOptions<boolean, boolean>, 'defaultInput'>
): (state?: boolean) => Promise<boolean>
```

#### Exemplos

```tsx
// Sistema de preferências
function UserPreferences() {
  const toggleDarkMode = useToggleInvoker('preferences:dark-mode')
  const toggleNotifications = useToggleInvoker('preferences:notifications')
  const toggleAutoSave = useToggleInvoker('preferences:auto-save')
  
  const [isDarkMode, setIsDarkMode] = useState(false)
  const [hasNotifications, setHasNotifications] = useState(true)
  const [hasAutoSave, setHasAutoSave] = useState(true)
  
  const handleDarkModeToggle = async () => {
    const newState = await toggleDarkMode(!isDarkMode)
    setIsDarkMode(newState)
  }
  
  const handleNotificationsToggle = async () => {
    const newState = await toggleNotifications(!hasNotifications)
    setHasNotifications(newState)
  }
  
  return (
    <div className="preferences">
      <label>
        <input
          type="checkbox"
          checked={isDarkMode}
          onChange={handleDarkModeToggle}
        />
        Modo Escuro
      </label>
      
      <label>
        <input
          type="checkbox"
          checked={hasNotifications}
          onChange={handleNotificationsToggle}
        />
        Notificações
      </label>
      
      <label>
        <input
          type="checkbox"
          checked={hasAutoSave}
          onChange={() => toggleAutoSave(!hasAutoSave).then(setHasAutoSave)}
        />
        Salvamento Automático
      </label>
    </div>
  )
}

// Controles de visibilidade
function VisibilityControls() {
  const toggleSidebar = useToggleInvoker('ui:toggle-sidebar')
  const toggleDevTools = useToggleInvoker('ui:toggle-devtools')
  const toggleFullscreen = useToggleInvoker('ui:toggle-fullscreen')
  
  return (
    <div className="ui-controls">
      <button onClick={() => toggleSidebar()}>
        Alternar Barra Lateral
      </button>
      
      <button onClick={() => toggleDevTools()}>
        DevTools
      </button>
      
      <button onClick={() => toggleFullscreen()}>
        Tela Cheia
      </button>
    </div>
  )
}

// Feature flags
function FeatureFlags() {
  const toggleFeature = useToggleInvoker('feature:toggle')
  const [features, setFeatures] = useState({
    newDashboard: false,
    betaEditor: false,
    experimentalApi: false
  })
  
  const handleToggleFeature = async (featureName) => {
    const newState = await toggleFeature({
      feature: featureName,
      enabled: !features[featureName]
    })
    
    setFeatures(prev => ({
      ...prev,
      [featureName]: newState
    }))
  }
  
  return (
    <div className="feature-flags">
      <h3>Features Experimentais</h3>
      
      {Object.entries(features).map(([feature, enabled]) => (
        <div key={feature}>
          <label>
            <input
              type="checkbox"
              checked={enabled}
              onChange={() => handleToggleFeature(feature)}
            />
            {feature}
          </label>
        </div>
      ))}
    </div>
  )
}
```

### 6. useBatchInvoker - Execução Sequencial

Executa múltiplos comandos em sequência, com controle de erro e progresso.

```tsx
useBatchInvoker(
  keys: CommandKey[],
  options?: {
    source?: CommandSource
    stopOnError?: boolean
    onProgress?: (completed: number, total: number) => void
  }
): (inputs?: any[], batchOptions?: { stopOnError?: boolean }) => Promise<ExecutionResult[]>
```

#### Exemplos

```tsx
// Processamento de múltiplos arquivos
function BatchFileProcessor() {
  const processFiles = useBatchInvoker(
    ['file:validate', 'file:optimize', 'file:upload', 'file:index'],
    {
      stopOnError: true,
      onProgress: (completed, total) => {
        console.log(`Processado ${completed}/${total}`)
      }
    }
  )
  
  const [progress, setProgress] = useState({ completed: 0, total: 0 })
  const [results, setResults] = useState([])
  
  const handleProcessFiles = async (files) => {
    const inputs = [
      { files },           // para validate
      { files },           // para optimize  
      { files },           // para upload
      { uploadedFiles: files } // para index
    ]
    
    const batchResults = await processFiles(inputs, {
      stopOnError: false // continua mesmo com erros
    })
    
    setResults(batchResults)
    
    const successful = batchResults.filter(r => r.success).length
    toast.info(`${successful}/${batchResults.length} operações concluídas`)
  }
  
  return (
    <div className="batch-processor">
      <FileDropzone onDrop={handleProcessFiles} />
      
      {results.length > 0 && (
        <div className="results">
          <h4>Resultados do Processamento:</h4>
          {results.map((result, idx) => (
            <div key={idx} className={result.success ? 'success' : 'error'}>
              Etapa {idx + 1}: {result.success ? '✅' : '❌'}
              {result.error && <span>{result.error.message}</span>}
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

// Pipeline de implantação
function DeploymentPipeline() {
  const deploySteps = useBatchInvoker(
    [
      'build:compile',
      'test:unit',
      'test:integration',
      'deploy:staging',
      'test:e2e',
      'deploy:production'
    ],
    {
      stopOnError: true, // para na primeira falha
      onProgress: (step, total) => {
        updateDeploymentStatus({
          currentStep: step,
          totalSteps: total,
          percentage: (step / total) * 100
        })
      }
    }
  )
  
  const [isDeploying, setIsDeploying] = useState(false)
  const [deploymentLog, setDeploymentLog] = useState([])
  
  const startDeployment = async () => {
    setIsDeploying(true)
    setDeploymentLog([])
    
    const config = {
      branch: 'main',
      environment: 'production',
      version: '2.0.0'
    }
    
    // Cada comando recebe a mesma config
    const inputs = Array(6).fill(config)
    
    const results = await deploySteps(inputs)
    
    // Processa resultados
    results.forEach((result, idx) => {
      const stepName = ['Compilação', 'Testes Unitários', 'Testes de Integração', 
                       'Deploy Staging', 'Testes E2E', 'Deploy Produção'][idx]
      
      setDeploymentLog(prev => [...prev, {
        step: stepName,
        success: result.success,
        error: result.error?.message,
        timestamp: new Date()
      }])
    })
    
    setIsDeploying(false)
    
    const allSuccessful = results.every(r => r.success)
    if (allSuccessful) {
      toast.success('Deploy concluído com sucesso!')
    } else {
      toast.error('Deploy falhou!')
    }
  }
  
  return (
    <div className="deployment-pipeline">
      <button 
        onClick={startDeployment}
        disabled={isDeploying}
      >
        {isDeploying ? 'Deployment em progresso...' : 'Iniciar Deploy'}
      </button>
      
      <div className="deployment-log">
        {deploymentLog.map((entry, idx) => (
          <div key={idx} className={`log-entry ${entry.success ? 'success' : 'error'}`}>
            <span>{entry.step}</span>
            <span>{entry.success ? '✅' : '❌'}</span>
            {entry.error && <span className="error-msg">{entry.error}</span>}
          </div>
        ))}
      </div>
    </div>
  )
}

// Wizard multi-etapas
function SetupWizard() {
  const setupSteps = useBatchInvoker([
    'setup:check-requirements',
    'setup:install-dependencies', 
    'setup:configure-database',
    'setup:create-admin',
    'setup:initial-data'
  ])
  
  const [currentStep, setCurrentStep] = useState(0)
  const [setupData, setSetupData] = useState({})
  
  const runSetup = async () => {
    const inputs = [
      { os: navigator.platform },
      { packageManager: 'npm' },
      { database: setupData.database },
      { admin: setupData.admin },
      { sampleData: setupData.includeSampleData }
    ]
    
    const results = await setupSteps(inputs, {
      stopOnError: true
    })
    
    if (results.every(r => r.success)) {
      router.push('/dashboard')
    }
  }
}
```

### 7. useParallelInvoker - Execução Paralela

Executa múltiplos comandos em paralelo, retornando Promise.allSettled results.

```tsx
useParallelInvoker(
  keys: CommandKey[],
  options?: { source?: CommandSource }
): (inputs?: any[]) => Promise<PromiseSettledResult<any>[]>
```

#### Exemplos

```tsx
// Carregamento paralelo de dados do dashboard
function DashboardData() {
  const loadDashboardData = useParallelInvoker([
    'stats:revenue',
    'stats:users',
    'stats:orders',
    'chart:sales',
    'notifications:unread'
  ])
  
  const [dashboardData, setDashboardData] = useState({
    revenue: null,
    users: null,
    orders: null,
    salesChart: null,
    notifications: null
  })
  
  const [isLoading, setIsLoading] = useState(true)
  
  useEffect(() => {
    const loadData = async () => {
      setIsLoading(true)
      
      // Inputs para cada comando
      const inputs = [
        { period: '30d' },        // revenue
        { status: 'active' },     // users
        { status: 'pending' },    // orders
        { range: 'last-month' },  // sales chart
        { limit: 10 }             // notifications
      ]
      
      const results = await loadDashboardData(inputs)
      
      // Processa resultados
      setDashboardData({
        revenue: results[0].status === 'fulfilled' ? results[0].value : null,
        users: results[1].status === 'fulfilled' ? results[1].value : null,
        orders: results[2].status === 'fulfilled' ? results[2].value : null,
        salesChart: results[3].status === 'fulfilled' ? results[3].value : null,
        notifications: results[4].status === 'fulfilled' ? results[4].value : null
      })
      
      setIsLoading(false)
      
      // Log de erros
      results.forEach((result, idx) => {
        if (result.status === 'rejected') {
          console.error(`Falha ao carregar ${['revenue', 'users', 'orders', 'chart', 'notifications'][idx]}:`, result.reason)
        }
      })
    }
    
    loadData()
  }, [])
  
  if (isLoading) return <DashboardSkeleton />
  
  return (
    <div className="dashboard">
      {dashboardData.revenue && <RevenueCard data={dashboardData.revenue} />}
      {dashboardData.users && <UsersCard data={dashboardData.users} />}
      {dashboardData.orders && <OrdersCard data={dashboardData.orders} />}
      {dashboardData.salesChart && <SalesChart data={dashboardData.salesChart} />}
      {dashboardData.notifications && <NotificationsList data={dashboardData.notifications} />}
    </div>
  )
}

// Busca em múltiplas fontes
function UniversalSearch() {
  const searchAllSources = useParallelInvoker([
    'search:users',
    'search:products',
    'search:orders',
    'search:documents',
    'search:help'
  ])
  
  const [searchResults, setSearchResults] = useState({})
  const [isSearching, setIsSearching] = useState(false)
  
  const handleSearch = async (query) => {
    if (!query.trim()) return
    
    setIsSearching(true)
    
    // Mesmo query para todas as fontes
    const inputs = Array(5).fill({ query, limit: 5 })
    
    const results = await searchAllSources(inputs)
    
    const processedResults = {
      users: results[0].status === 'fulfilled' ? results[0].value : [],
      products: results[1].status === 'fulfilled' ? results[1].value : [],
      orders: results[2].status === 'fulfilled' ? results[2].value : [],
      documents: results[3].status === 'fulfilled' ? results[3].value : [],
      help: results[4].status === 'fulfilled' ? results[4].value : []
    }
    
    setSearchResults(processedResults)
    setIsSearching(false)
  }
  
  return (
    <div className="universal-search">
      <SearchInput 
        onSearch={handleSearch}
        placeholder="Buscar em todo o sistema..."
      />
      
      {isSearching && <SearchSpinner />}
      
      <div className="search-results">
        {Object.entries(searchResults).map(([category, items]) => (
          items.length > 0 && (
            <div key={category} className="result-category">
              <h4>{category}</h4>
              {items.map(item => (
                <SearchResultItem key={item.id} item={item} />
              ))}
            </div>
          )
        ))}
      </div>
    </div>
  )
}

// Validação paralela de formulário
function ComplexFormValidation() {
  const validateFields = useParallelInvoker([
    'validate:email',
    'validate:cpf',
    'validate:phone',
    'validate:address',
    'validate:credit-card'
  ])
  
  const [errors, setErrors] = useState({})
  const [isValidating, setIsValidating] = useState(false)
  
  const validateForm = async (formData) => {
    setIsValidating(true)
    
    const inputs = [
      { email: formData.email },
      { cpf: formData.cpf },
      { phone: formData.phone },
      { address: formData.address },
      { creditCard: formData.creditCard }
    ]
    
    const results = await validateFields(inputs)
    
    const validationErrors = {}
    const fieldNames = ['email', 'cpf', 'phone', 'address', 'creditCard']
    
    results.forEach((result, idx) => {
      if (result.status === 'rejected' || 
          (result.status === 'fulfilled' && !result.value.isValid)) {
        validationErrors[fieldNames[idx]] = 
          result.status === 'rejected' 
            ? 'Erro ao validar campo'
            : result.value.error
      }
    })
    
    setErrors(validationErrors)
    setIsValidating(false)
    
    return Object.keys(validationErrors).length === 0
  }
  
  return { validateForm, errors, isValidating }
}
```

## 🆚 Comparação com useCommand

| Aspecto | useCommand | useInvoker |
|---------|------------|------------|
| **Retorno** | CommandInvoker (objeto) | Função direta |
| **Uso** | `cmd.invoke(data)` | `cmd(data)` |
| **Estado** | Sempre disponível | Use `useCommand` |
| **Configurações** | Todas disponíveis | Essenciais apenas |
| **Retry/Debounce** | ✅ Sim | ❌ Não |
| **Transformações** | ✅ Sim | ❌ Não |
| **Casos de Uso** | Complexos | Simples e diretos |

### Quando Usar Cada Um

```tsx
// Use useCommand quando precisar de:
// ✅ Estado (isLoading, lastResult, etc)
// ✅ Múltiplos métodos (invoke, attempt, execute)
// ✅ Configurações avançadas (retry, debounce, throttle)
// ✅ Transformações de dados
// ✅ Validação de input

const command = useCommand('complex:operation', {
  retry: 3,
  debounce: 500,
  transformInput: (input) => ({ ...input, timestamp: Date.now() }),
  validateInput: (input) => input.name ? true : 'Nome obrigatório'
})

// Acesso ao estado
console.log(command.isLoading)
console.log(command.lastResult)

// Múltiplos métodos
await command.invoke(data)    // Lança erro se falhar
await command.attempt(data)   // Retorna ExecutionResult
await command.execute(data)   // Com callbacks inline

// Use useInvoker quando quiser:
// ✅ Execução direta e simples
// ✅ Sintaxe mínima
// ✅ Sem necessidade de estado
// ✅ Performance otimizada

const save = useInvoker('file:save')
await save(data)  // Simples e direto!

// Para ações específicas
const logout = useAction('user:logout')
await logout()

const safeCall = useSafeInvoker('risky:operation')
const result = await safeCall()  // Nunca lança erro
```

## 🎯 Casos de Uso Avançados

### 1. Sistema de Notificações

```tsx
function NotificationSystem() {
  // Ações diretas para tipos de notificação
  const showSuccess = useAction('notification:success')
  const showError = useAction('notification:error')
  const showWarning = useAction('notification:warning')
  
  // Notificação customizada com input bound
  const showCustom = useBoundInvoker('notification:custom', {
    duration: 5000,
    position: 'top-right',
    dismissible: true
  })
  
  // Toast com estado para controle
  const toast = useCommandState('notification:toast')
  
  const notify = {
    success: (message) => showSuccess({ message }),
    error: (message) => showError({ message }),
    warning: (message) => showWarning({ message }),
    
    custom: (message, options = {}) => showCustom({
      message,
      ...options
    }),
    
    progress: (message) => toast.invoke({
      type: 'progress',
      message,
      showProgress: true
    })
  }
  
  return { notify, toast }
}

// Uso no app
function App() {
  const { notify } = useNotificationSystem()
  
  const handleSave = async () => {
    notify.progress('Salvando documento...')
    
    try {
      await saveDocument()
      notify.success('Documento salvo!')
    } catch (error) {
      notify.error('Erro ao salvar: ' + error.message)
    }
  }
}
```

### 2. Sistema de Cache Inteligente

```tsx
function SmartCache() {
  // Cache hit/miss com estado
  const cacheGet = useCommandState('cache:get')
  const cacheSet = useSafeInvoker('cache:set')
  const cacheInvalidate = useAction('cache:invalidate')
  
  const useCache = (key, fetcher, options = {}) => {
    const [data, setData] = useState(null)
    const [loading, setLoading] = useState(true)
    
    useEffect(() => {
      const loadData = async () => {
        setLoading(true)
        
        // Tenta cache primeiro
        const cached = await cacheGet.invoke({ key })
        
        if (cached && !isExpired(cached, options.ttl)) {
          setData(cached.data)
          setLoading(false)
          return
        }
        
        // Cache miss - busca dados
        try {
          const freshData = await fetcher()
          setData(freshData)
          
          // Salva no cache (safe - não quebra se falhar)
          await cacheSet({ 
            key, 
            data: freshData, 
            timestamp: Date.now() 
          })
        } catch (error) {
          console.error('Fetch failed:', error)
        } finally {
          setLoading(false)
        }
      }
      
      loadData()
    }, [key])
    
    const invalidate = () => {
      cacheInvalidate({ key })
      setData(null)
    }
    
    return { data, loading, invalidate, cacheHit: cacheGet.lastResult }
  }
  
  return { useCache }
}

// Uso
function UserProfile({ userId }) {
  const { useCache } = useSmartCache()
  
  const { data: user, loading, invalidate } = useCache(
    `user:${userId}`,
    () => fetchUser(userId),
    { ttl: 5 * 60 * 1000 } // 5 minutos
  )
  
  if (loading) return <Spinner />
  
  return (
    <div>
      <UserInfo user={user} />
      <button onClick={invalidate}>Atualizar</button>
    </div>
  )
}
```

### 3. Upload Manager

```tsx
function UploadManager() {
  // Upload único com estado completo
  const singleUpload = useCommandState('file:upload-single')
  
  // Upload em lote (seguro para continuar mesmo com falhas)
  const batchUpload = useSafeInvoker('file:upload-batch')
  
  // Operações bound ao usuário atual
  const currentUser = useCurrentUser()
  const uploadToGallery = useBoundInvoker('file:upload-gallery', {
    userId: currentUser.id,
    gallery: 'user-uploads'
  })
  
  const uploadToProject = useBoundInvoker('file:upload-project', {
    userId: currentUser.id
  })
  
  const uploadStates = new Map()
  
  const uploadFile = async (file, destination = 'general') => {
    const uploadId = generateId()
    
    switch (destination) {
      case 'gallery':
        return await uploadToGallery({ file, uploadId })
        
      case 'project':
        const projectId = getCurrentProject().id
        return await uploadToProject({ file, uploadId, projectId })
        
      default:
        return await singleUpload.invoke({ file, uploadId })
    }
  }
  
  const uploadMultiple = async (files) => {
    const result = await batchUpload(files.map(file => ({
      file,
      uploadId: generateId()
    })))
    
    if (result.success) {
      return result.result
    } else {
      // Processa resultados parciais
      const { successful, failed } = result.error.partialResults || {}
      
      toast.info(`${successful?.length || 0} arquivos enviados, ${failed?.length || 0} falharam`)
      
      return { successful, failed }
    }
  }
  
  return {
    uploadFile,
    uploadMultiple,
    singleUploadState: singleUpload,
    clearUploadHistory: () => singleUpload.reset()
  }
}

// Componente de upload
function FileUploadZone() {
  const { uploadFile, uploadMultiple, singleUploadState } = useUploadManager()
  
  const handleDrop = async (files) => {
    if (files.length === 1) {
      await uploadFile(files[0], 'gallery')
    } else {
      await uploadMultiple(files)
    }
  }
  
  return (
    <div 
      className="upload-zone"
      onDrop={handleDrop}
      data-uploading={singleUploadState.isLoading}
    >
      {singleUploadState.isLoading ? (
        <div className="upload-progress">
          <Spinner />
          <span>Enviando arquivo...</span>
        </div>
      ) : (
        <div className="upload-hint">
          Arraste arquivos aqui ou clique para selecionar
        </div>
      )}
      
      {singleUploadState.lastError && (
        <div className="upload-error">
          ❌ {singleUploadState.lastError.message}
          <button onClick={singleUploadState.clearError}>×</button>
        </div>
      )}
    </div>
  )
}
```

## 🎭 Patterns Avançados

### 1. Command Factory Pattern

```tsx
function useCommandFactory() {
  const createCRUDCommands = (entity) => ({
    create: useBoundInvoker(`${entity}:create`, { 
      createdBy: getCurrentUser().id 
    }),
    
    update: useBoundInvoker(`${entity}:update`, { 
      updatedBy: getCurrentUser().id 
    }),
    
    delete: useSafeInvoker(`${entity}:delete`),
    
    list: useCommandState(`${entity}:list`),
    
    get: useInvoker(`${entity}:get`)
  })
  
  return { createCRUDCommands }
}

// Uso
function UserManagement() {
  const { createCRUDCommands } = useCommandFactory()
  const userCommands = createCRUDCommands('user')
  
  return (
    <div>
      <button onClick={() => userCommands.create({ name: 'João' })}>
        Criar Usuário
      </button>
      
      <button onClick={() => userCommands.list.invoke()}>
        Listar Usuários
      </button>
      
      {userCommands.list.isLoading && <Spinner />}
    </div>
  )
}
```

### 2. State Machine Integration

```tsx
function useWorkflowState() {
  const transitions = {
    start: useAction('workflow:start'),
    pause: useAction('workflow:pause'),
    resume: useAction('workflow:resume'),
    complete: useAction('workflow:complete'),
    cancel: useAction('workflow:cancel')
  }
  
  const status = useCommandState('workflow:status')
  
  const canTransition = (from, to) => {
    const validTransitions = {
      'idle': ['start'],
      'running': ['pause', 'complete', 'cancel'],
      'paused': ['resume', 'cancel'],
      'completed': ['start'],
      'cancelled': ['start']
    }
    
    return validTransitions[from]?.includes(to) || false
  }
  
  return { transitions, status, canTransition }
}
```

## 🎯 Resumo

A família `useInvoker` fornece acesso direto e simplificado aos comandos:

### Hooks de Execução Direta
- **`useInvoker`** - Retorna diretamente a função de execução
- **`useAction`** - Para comandos sem parâmetros
- **`useSafeInvoker`** - Execução que nunca lança erro
- **`useBoundInvoker`** - Com input pré-configurado
- **`useToggleInvoker`** - Para comandos toggle (boolean)

### Hooks de Execução em Lote
- **`useBatchInvoker`** - Execução sequencial com controle de progresso
- **`useParallelInvoker`** - Execução paralela com Promise.allSettled

### Hook de Estado
- **`useCommandState`** - Alias para useCommand (considere usar useCommand diretamente)

Para casos que necessitam de estado, múltiplos métodos de execução ou configurações avançadas, use `useCommand` diretamente.