# useCustomCommand - Guia Completo

> 🎯 **O hook principal para criar comandos temporários em componentes React**

## 📋 Índice

1. [Conceito](#conceito)
2. [Anatomia do Hook](#anatomia-do-hook)
3. [Ciclo de Vida](#ciclo-de-vida)
4. [Sistema de Disponibilidade](#sistema-de-disponibilidade)
5. [Retorno do Hook (Invoker)](#retorno-do-hook-invoker)
6. [Hooks Especializados](#hooks-especializados)
7. [Padrões Avançados](#padrões-avançados)
8. [Performance e Otimização](#performance-e-otimização)
9. [Troubleshooting](#troubleshooting)

---

## Conceito

O `useCustomCommand` é um React Hook que permite registrar comandos temporários que:

- ✅ **Existem apenas enquanto o componente está montado**
- ✅ **Aparecem automaticamente no Command Palette**
- ✅ **São removidos quando o componente desmonta**
- ✅ **Mantêm type safety com TypeScript**
- ✅ **Integram com o estado do componente**

### Por que usar?

```tsx
// ❌ Sem useCustomCommand - ações espalhadas
function Modal({ onSave, onClose }) {
  return (
    <div>
      <button onClick={onSave}>Salvar (Ctrl+S)</button>
      <button onClick={onClose}>Fechar (ESC)</button>
    </div>
  )
}

// ✅ Com useCustomCommand - ações centralizadas e descobríveis
function Modal({ onSave, onClose }) {
  useCustomCommand({
    key: 'modal:save',
    label: 'Salvar',
    shortcut: 'ctrl+s',
    handle: async () => onSave()
  })

  useCustomCommand({
    key: 'modal:close',
    label: 'Fechar',
    shortcut: 'escape',
    handle: async () => onClose()
  })

  // Comandos disponíveis via Ctrl+Shift+P
  return <div>{/* ... */}</div>
}
```

---

## Anatomia do Hook

### Interface TypeScript

```typescript
interface UseCustomCommandProps<TInput = any, TOutput = any> {
  // Identificador único do comando
  key: string
  
  // Label exibido no Command Palette
  label?: string
  
  // Descrição detalhada
  description?: string
  
  // Categoria para organização
  category?: CommandCategory
  
  // Tags para busca
  tags?: string[]
  
  // Ícone visual (emoji ou componente)
  icon?: string
  
  // Atalho de teclado
  shortcut?: string
  
  // Função que determina disponibilidade
  when?: () => boolean | Promise<boolean>
  
  // Handler assíncrono do comando
  handle: (input?: TInput) => Promise<TOutput>
  
  // Timeout em ms (padrão: 30000)
  timeout?: number
  
  // Prioridade na busca (maior = primeiro)
  priority?: number
  
  // Proprietário do comando
  owner?: string
  
  // Palavras-chave adicionais para busca
  searchKeywords?: string[]
}
```

### Uso Básico

```tsx
function MyComponent() {
  const [data, setData] = useState(null)

  const saveCommand = useCustomCommand({
    key: 'my:save',
    label: 'Salvar Dados',
    handle: async () => {
      const result = await api.save(data)
      return result
    }
  })

  return (
    <button onClick={() => saveCommand.invoke()}>
      Salvar
    </button>
  )
}
```

---

## Ciclo de Vida

### 1. **Registro (Mount)**

Quando o componente monta, o comando é registrado automaticamente:

```tsx
function FileEditor({ fileId }) {
  // Comando registrado quando FileEditor monta
  useCustomCommand({
    key: `file:save:${fileId}`,
    label: 'Salvar Arquivo',
    handle: async () => saveFile(fileId)
  })
  
  // Se fileId mudar, um novo comando é registrado
  // e o anterior é removido
}
```

### 2. **Atualização (Update)**

O comando é re-registrado quando suas dependências mudam:

```tsx
function DynamicCommand() {
  const [mode, setMode] = useState('view')

  // Re-registra quando 'mode' muda
  useCustomCommand({
    key: 'action',
    label: mode === 'edit' ? 'Salvar' : 'Editar',
    handle: async () => {
      if (mode === 'edit') {
        await save()
        setMode('view')
      } else {
        setMode('edit')
      }
    }
  })
}
```

### 3. **Remoção (Unmount)**

O comando é removido automaticamente quando o componente desmonta:

```tsx
function TemporaryFeature({ isVisible }) {
  if (!isVisible) return null

  // Comando existe apenas quando isVisible = true
  useCustomCommand({
    key: 'temp:action',
    label: 'Ação Temporária',
    handle: async () => doSomething()
  })

  return <div>Feature Temporária</div>
}
```

---

## Sistema de Disponibilidade

### A função `when`

A função `when` determina se o comando está disponível:

```tsx
useCustomCommand({
  key: 'save',
  label: 'Salvar',
  when: () => hasChanges && !isSaving,
  handle: async () => save()
})
```

### Quando é avaliada

1. **Durante busca** - Para filtrar comandos indisponíveis
2. **Antes da execução** - Para validar se ainda pode executar
3. **Na UI** - Para mostrar estado desabilitado

### Importante: Não é reativa!

```tsx
// ❌ ERRADO - Pensa que vai atualizar automaticamente
function BadExample() {
  const [canSave, setCanSave] = useState(false)

  useCustomCommand({
    key: 'save',
    when: () => canSave, // Não re-avalia sozinho!
    handle: async () => save()
  })
}

// ✅ CORRETO - Re-registra quando estado muda
function GoodExample() {
  const [canSave, setCanSave] = useState(false)

  // Re-registra o comando quando canSave muda
  const command = useMemo(() => ({
    key: 'save',
    label: canSave ? 'Salvar' : 'Salvar (desabilitado)',
    when: () => canSave,
    handle: async () => save()
  }), [canSave])

  useCustomCommand(command)
}
```

### Padrões de disponibilidade

```tsx
// Baseado em permissões
when: () => user.role === 'admin'

// Baseado em estado
when: () => form.isValid && !form.isSubmitting

// Baseado em contexto
when: () => selectedItems.length > 0

// Assíncrono (use com cuidado!)
when: async () => {
  const hasPermission = await checkPermission()
  return hasPermission
}
```

---

## Retorno do Hook (Invoker)

O hook retorna um objeto `invoker` com métodos úteis:

```tsx
interface CustomCommandInvoker<TInput, TOutput> {
  // Chave do comando
  key: CommandKey
  
  // Verifica se comando existe
  exists: () => boolean
  
  // Verifica disponibilidade
  isAvailable: () => Promise<boolean>
  
  // Executa o comando
  invoke: (input?: TInput, source?: string) => Promise<TOutput>
  
  // Executa com tratamento de erro
  attempt: (input?: TInput, source?: string) => Promise<ExecutionResult<TOutput>>
  
  // Obtém o comando registrado
  getCommand: () => Command<TInput, TOutput> | undefined
}
```

### Exemplos de uso

```tsx
function MyComponent() {
  const saveCommand = useCustomCommand({
    key: 'save',
    label: 'Salvar',
    handle: async (data: SaveData) => {
      return await api.save(data)
    }
  })

  // Executar diretamente
  const handleSave = async () => {
    const result = await saveCommand.invoke({ title: 'Teste' })
    console.log('Salvo:', result)
  }

  // Executar com tratamento de erro
  const handleSafeSave = async () => {
    const result = await saveCommand.attempt({ title: 'Teste' })
    
    if (result.success) {
      console.log('Sucesso:', result.result)
    } else {
      console.error('Erro:', result.error)
    }
  }

  // Verificar disponibilidade
  const checkCommand = async () => {
    if (await saveCommand.isAvailable()) {
      console.log('Comando disponível')
    }
  }

  // Acessar comando completo
  const command = saveCommand.getCommand()
  console.log('Atalho:', command?.shortcut)
}
```

---

## Hooks Especializados

### useAction

Para ações simples sem input/output:

```tsx
function Toolbar() {
  const refresh = useAction(
    'refresh',
    'Atualizar',
    () => {
      window.location.reload()
    },
    {
      icon: '🔄',
      shortcut: 'f5'
    }
  )

  return <button onClick={() => refresh.invoke()}>Refresh</button>
}
```

### useToggleCommand

Para comandos toggle (on/off):

```tsx
function Settings() {
  const [darkMode, setDarkMode] = useState(false)

  const toggleDark = useToggleCommand(
    'dark-mode',
    'Modo Escuro',
    darkMode,
    setDarkMode,
    {
      icon: darkMode ? '🌙' : '☀️',
      shortcut: 'ctrl+shift+d'
    }
  )

  // Label automático: "Disable Modo Escuro" / "Enable Modo Escuro"
}
```

### useModalCommand

Para comandos que abrem modais:

```tsx
function UserList() {
  const createUser = useModalCommand(
    'user:create',
    'Novo Usuário',
    async () => {
      const userData = await showCreateUserModal()
      if (userData) {
        return await api.createUser(userData)
      }
    },
    {
      icon: '👤',
      shortcut: 'ctrl+n'
    }
  )
}
```

### useNavigationCommand

Para navegação:

```tsx
function ProductPage() {
  const router = useRouter()

  useNavigationCommand(
    'go:home',
    'Ir para Home',
    '/',
    router.push,
    {
      icon: '🏠',
      shortcut: 'alt+h'
    }
  )
}
```

### useContextualCommands

Para listas dinâmicas:

```tsx
function TodoList({ todos }) {
  // Cria um comando para cada todo
  useContextualCommands(
    todos,
    (todo) => ({
      key: `todo:complete:${todo.id}`,
      label: `Completar: ${todo.title}`,
      icon: '✅',
      handle: async () => completeTodo(todo.id)
    })
  )
}
```

---

## Padrões Avançados

### 1. **Comandos com Estado**

```tsx
function StatefulCommand() {
  const [lastExecution, setLastExecution] = useState(null)

  useCustomCommand({
    key: 'stateful',
    label: lastExecution 
      ? `Última execução: ${lastExecution}`
      : 'Executar',
    handle: async () => {
      const now = new Date().toLocaleTimeString()
      setLastExecution(now)
      return { executedAt: now }
    }
  })
}
```

### 2. **Comandos Compostos**

```tsx
function BulkActions({ items }) {
  const [processing, setProcessing] = useState(false)

  useCustomCommand({
    key: 'bulk:process',
    label: `Processar ${items.length} itens`,
    when: () => items.length > 0 && !processing,
    handle: async () => {
      setProcessing(true)
      
      const results = []
      for (const item of items) {
        results.push(await processItem(item))
      }
      
      setProcessing(false)
      return { processed: results.length }
    }
  })
}
```

### 3. **Comandos com Confirmação**

```tsx
function DangerousAction() {
  const deleteCommand = useCustomCommand({
    key: 'delete:all',
    label: 'Deletar Tudo',
    icon: '⚠️',
    category: 'danger',
    priority: -10, // Baixa prioridade
    handle: async () => {
      const confirmed = await showConfirmDialog({
        title: 'Deletar Tudo?',
        message: 'Esta ação não pode ser desfeita.',
        confirmText: 'Deletar',
        danger: true
      })
      
      if (confirmed) {
        await api.deleteAll()
        return { deleted: true }
      }
      
      return { deleted: false }
    }
  })
}
```

### 4. **Comandos com Loading State**

```tsx
function AsyncCommand() {
  const [isLoading, setIsLoading] = useState(false)

  const command = useCustomCommand({
    key: 'async:operation',
    label: isLoading ? 'Processando...' : 'Iniciar Processo',
    when: () => !isLoading,
    handle: async () => {
      setIsLoading(true)
      try {
        const result = await longRunningOperation()
        return result
      } finally {
        setIsLoading(false)
      }
    }
  })
}
```

### 5. **Comandos com Progresso**

```tsx
function ProgressCommand() {
  const [progress, setProgress] = useState(0)

  useCustomCommand({
    key: 'upload',
    label: progress > 0 ? `Upload: ${progress}%` : 'Fazer Upload',
    when: () => progress === 0,
    handle: async () => {
      const file = await selectFile()
      
      return uploadWithProgress(file, (percent) => {
        setProgress(percent)
      }).finally(() => {
        setProgress(0)
      })
    }
  })
}
```

---

## Performance e Otimização

### 1. **Memoização de Comandos**

```tsx
// ❌ Re-cria comando a cada render
function BadPerformance() {
  useCustomCommand({
    key: 'action',
    label: 'Action',
    handle: async () => {
      // Complex logic
    }
  })
}

// ✅ Memoiza comando pesado
function GoodPerformance() {
  const command = useMemo(() => ({
    key: 'action',
    label: 'Action',
    handle: async () => {
      // Complex logic
    }
  }), [/* dependências */])

  useCustomCommand(command)
}
```

### 2. **Callbacks Estáveis**

```tsx
function StableCallbacks() {
  const [data, setData] = useState(null)

  // ✅ useCallback para handler estável
  const handleSave = useCallback(async () => {
    return await api.save(data)
  }, [data])

  useCustomCommand({
    key: 'save',
    label: 'Save',
    handle: handleSave
  })
}
```

### 3. **Evitar Re-registros**

```tsx
// ❌ Re-registra a cada mudança de count
function TooManyRegistrations({ count }) {
  useCustomCommand({
    key: 'show-count',
    label: `Count: ${count}`,
    handle: async () => alert(count)
  })
}

// ✅ Usa closure para acessar valor atual
function OptimizedRegistration({ count }) {
  const countRef = useRef(count)
  countRef.current = count

  const command = useMemo(() => ({
    key: 'show-count',
    label: 'Show Count',
    handle: async () => alert(countRef.current)
  }), []) // Sem dependências!

  useCustomCommand(command)
}
```

### 4. **Batch de Comandos**

```tsx
// ❌ Muitas chamadas individuais
function ManyCommands({ items }) {
  items.forEach(item => {
    useCustomCommand({
      key: `item:${item.id}`,
      label: item.name,
      handle: async () => processItem(item)
    })
  })
}

// ✅ Use useContextualCommands
function OptimizedCommands({ items }) {
  useContextualCommands(items, (item) => ({
    key: `item:${item.id}`,
    label: item.name,
    handle: async () => processItem(item)
  }))
}
```

---

## Troubleshooting

### Comando não aparece no Command Palette

```tsx
// Verifique:
// 1. Key única
useCustomCommand({
  key: `unique:key:${id}`, // ✅ Única por instância
  // ...
})

// 2. Label definido
useCustomCommand({
  key: 'test',
  label: 'Test Command', // ✅ Obrigatório
  // ...
})

// 3. Função when retornando true
useCustomCommand({
  key: 'test',
  when: () => {
    console.log('Checking availability') // Debug
    return true
  },
  // ...
})
```

### Comando não executa

```tsx
// Verifique erros no console
useCustomCommand({
  key: 'test',
  handle: async () => {
    try {
      // Sua lógica
    } catch (error) {
      console.error('Command error:', error)
      throw error // Re-throw para o Commander capturar
    }
  }
})
```

### Memory leaks

```tsx
// ❌ Criar listeners sem cleanup
useCustomCommand({
  key: 'listen',
  handle: async () => {
    window.addEventListener('resize', handler) // Leak!
  }
})

// ✅ Cleanup adequado
useEffect(() => {
  const handler = () => {}
  
  useCustomCommand({
    key: 'listen',
    handle: async () => {
      // Use o handler
    }
  })
  
  window.addEventListener('resize', handler)
  return () => window.removeEventListener('resize', handler)
}, [])
```

### Performance ruim

```tsx
// Use React DevTools Profiler para identificar:
// 1. Re-renders excessivos
// 2. Comandos sendo re-registrados
// 3. Handlers pesados sem memoização

// Habilite logs de debug:
if (process.env.NODE_ENV === 'development') {
  console.log('Registering command:', key)
}
```

---

## 🎯 Resumo

- **useCustomCommand** cria comandos temporários vinculados ao ciclo de vida do componente
- **when** não é reativo - re-registre o comando quando o estado mudar
- **Invoker** retornado permite execução programática
- **Hooks especializados** simplificam casos comuns
- **Memoização** é importante para performance
- **IDs únicos** previnem conflitos entre instâncias

O hook transforma componentes React em interfaces poderosas e acessíveis, onde cada ação importante pode ser descoberta e executada via Command Palette.