# useCommand Hook - Guia Completo

O `useCommand` é o hook fundamental para executar comandos existentes no Commander com controle total sobre configurações, estado e callbacks.

## 📋 Índice

- [Visão Geral](#visão-geral)
- [Assinaturas e Tipos](#assinaturas-e-tipos)
- [Configurações](#configurações)
- [Exemplos Práticos](#exemplos-práticos)
- [Estado e Tracking](#estado-e-tracking)
- [Tratamento de Erros](#tratamento-de-erros)
- [Performance e Otimizações](#performance-e-otimizações)

## 🎯 Visão Geral

O `useCommand` oferece múltiplas formas de uso, desde execução simples até controle completo com tracking de estado:

```tsx
// 1. Execução simples
const saveFile = useCommand("file:save");
await saveFile({ filename: "doc.txt" });

// 2. Com configurações
const saveFile = useCommand("file:save", {
  throwOnError: false,
  timeout: 5000,
});

// 3. Com tracking de estado completo
const saveFile = useCommand("file:save", {
  trackState: true,
});
console.log(saveFile.isLoading, saveFile.lastResult);

// 4. Objeto completo de controle
const saveFile = useCommand("file:save", {
  returnInvoker: true,
});
```

## 🔧 Assinaturas e Tipos

### Assinaturas Disponíveis

```tsx
// 1. Básico - retorna função invoke
useCommand<TInput, TOutput>(key: CommandKey): (input?: TInput) => Promise<TOutput>

// 2. Com opções - retorna função configurada
useCommand<TInput, TOutput>(
  key: CommandKey,
  options: UseCommandOptions & { returnInvoker?: false }
): (input?: TInput) => Promise<TOutput>

// 3. Objeto completo - returnInvoker: true
useCommand<TInput, TOutput>(
  key: CommandKey,
  options: UseCommandOptions & { returnInvoker: true }
): CommandInvoker<TInput, TOutput>

// 4. Com tracking - trackState: true
useCommand<TInput, TOutput>(
  key: CommandKey,
  options: UseCommandOptions & { trackState: true }
): CommandInvoker<TInput, TOutput>
```

### Interface CommandInvoker

```tsx
interface CommandInvoker<TInput, TOutput> {
  // Estado do comando
  exists: boolean;
  isAvailable: boolean;
  isLoading: boolean;

  // Resultados e histórico
  lastResult: TOutput | null;
  lastError: Error | null;
  lastInput: TInput | null;
  lastExecution: Date | null;
  executionCount: number;

  // Comando completo
  command: Command<TInput, TOutput> | null;

  // Métodos de execução
  invoke: (input?: TInput) => Promise<TOutput>;
  attempt: (input?: TInput) => Promise<ExecutionResult<TOutput>>;
  execute: (
    input?: TInput,
    callbacks?: ExecuteCallbacks,
  ) => Promise<TOutput | null>;

  // Verificações
  canInvoke: (input?: TInput) => Promise<boolean>;
  validateInput: (input?: TInput) => boolean | string;

  // Utilitários
  reset: () => void;
  clearError: () => void;
  refresh: () => void;
}
```

## ⚙️ Configurações

### UseCommandOptions Interface

```tsx
interface UseCommandOptions<TInput, TOutput> {
  // Execução
  source?: "palette" | "shortcut" | "api"; // Default: 'api'
  throwOnError?: boolean; // Default: true
  timeout?: number; // Timeout em ms

  // Retry & Debounce
  retry?: number; // Tentativas (default: 0)
  retryDelay?: number | ((attempt: number) => number);
  debounce?: number; // Debounce em ms
  throttle?: number; // Throttle em ms

  // Input handling
  defaultInput?: Partial<TInput>; // Input padrão
  validateInput?: (input?: TInput) => boolean | string;
  transformInput?: (input?: TInput) => TInput;

  // Output handling
  transformOutput?: (output: TOutput) => TOutput;

  // Callbacks
  onSuccess?: (result: TOutput, input?: TInput) => void;
  onError?: (error: Error, input?: TInput) => void;
  onFinally?: (input?: TInput) => void;

  // Estado
  trackState?: boolean; // Ativa tracking
  returnInvoker?: boolean; // Retorna objeto completo
  resetOnKeyChange?: boolean; // Reset ao mudar key
}
```

## 📚 Exemplos Práticos

### 1. Execução Básica

```tsx
function FileEditor() {
  // Forma mais simples
  const saveFile = useCommand<SaveInput, SaveResult>("file:save");

  const handleSave = async () => {
    try {
      const result = await saveFile({
        filename: "document.txt",
        content: editorContent,
      });
      toast.success(`Salvo: ${result.filename}`);
    } catch (error) {
      toast.error("Erro ao salvar");
    }
  };

  return <button onClick={handleSave}>Save</button>;
}
```

### 2. Com Configurações Avançadas

```tsx
function RobustUploader() {
  const uploadFile = useCommand("file:upload", {
    retry: 3, // 3 tentativas
    retryDelay: (attempt) => attempt * 1000, // Delay progressivo
    timeout: 30000, // 30s timeout
    throwOnError: false, // Não lança erro

    defaultInput: {
      quality: "high",
      compress: true,
    },

    validateInput: (input) => {
      if (!input?.file) return "Arquivo obrigatório";
      if (input.file.size > 10_000_000) return "Arquivo muito grande";
      return true;
    },

    transformInput: (input) => ({
      ...input,
      uploadedAt: new Date(),
      userId: getCurrentUser().id,
    }),

    onSuccess: (result, input) => {
      analytics.track("file_uploaded", {
        size: input?.file.size,
        duration: result.duration,
      });
    },

    onError: (error, input) => {
      logger.error("Upload failed", { error, filename: input?.file.name });
    },
  });

  const handleUpload = async (file: File) => {
    const result = await uploadFile({ file });
    if (result) {
      setUploadedFiles((prev) => [...prev, result]);
    }
  };
}
```

### 3. Com Tracking de Estado

```tsx
function SmartFileUploader() {
  const uploader = useCommand("file:upload", {
    trackState: true,
    debounce: 500, // Evita uploads múltiplos

    onSuccess: (result) => {
      toast.success(`Upload concluído: ${result.filename}`);
    },
  });

  return (
    <div>
      <input
        type="file"
        onChange={(e) => uploader.invoke({ file: e.target.files[0] })}
        disabled={uploader.isLoading || !uploader.isAvailable}
      />

      {/* Estado visual */}
      {uploader.isLoading && (
        <div className="flex items-center gap-2">
          <Spinner />
          <span>Enviando arquivo...</span>
        </div>
      )}

      {/* Histórico */}
      {uploader.lastResult && (
        <div className="text-green-600">
          ✅ Último upload: {uploader.lastResult.filename}
          <br />
          📊 Total de uploads: {uploader.executionCount}
        </div>
      )}

      {/* Erro */}
      {uploader.lastError && (
        <div className="text-red-600">
          ❌ Erro: {uploader.lastError.message}
          <button onClick={uploader.clearError}>Limpar</button>
        </div>
      )}

      {/* Controles */}
      <div className="flex gap-2 mt-4">
        <button onClick={() => uploader.reset()} disabled={uploader.isLoading}>
          Reset Estado
        </button>

        <button onClick={uploader.refresh}>Verificar Disponibilidade</button>
      </div>
    </div>
  );
}
```

### 4. Validação e Transformação

```tsx
function UserProfileEditor() {
  const updateProfile = useCommand("user:update", {
    // Validação personalizada
    validateInput: (input) => {
      const errors = [];
      if (!input?.email?.includes("@")) errors.push("Email inválido");
      if (!input?.name?.trim()) errors.push("Nome obrigatório");
      return errors.length > 0 ? errors.join(", ") : true;
    },

    // Transformação de input
    transformInput: (input) => ({
      ...input,
      email: input.email?.toLowerCase(),
      name: input.name?.trim(),
      updatedAt: new Date().toISOString(),
    }),

    // Transformação de output
    transformOutput: (result) => ({
      ...result,
      avatarUrl: result.avatar ? `/avatars/${result.avatar}` : null,
    }),

    trackState: true,
  });

  const handleSubmit = async (formData) => {
    // A validação ocorre automaticamente
    const result = await updateProfile.invoke(formData);
    if (result) {
      // Output já foi transformado
      setUser(result);
    }
  };
}
```

### 5. Execução com Callbacks Inline

```tsx
function DocumentProcessor() {
  const processDoc = useCommand("document:process", {
    returnInvoker: true,
    timeout: 60000, // 1 minuto para processamento
  });

  const handleProcess = async (document) => {
    // Usando execute com callbacks inline
    const result = await processDoc.execute(
      { documentId: document.id },
      {
        onSuccess: (result) => {
          notification.success({
            title: "Processamento Concluído",
            message: `Documento processado em ${result.duration}ms`,
          });
        },

        onError: (error) => {
          notification.error({
            title: "Erro no Processamento",
            message: error.message,
            action: {
              label: "Tentar Novamente",
              onClick: () => handleProcess(document),
            },
          });
        },

        onFinally: () => {
          analytics.track("document_process_attempt", {
            documentId: document.id,
          });
        },
      },
    );
  };

  // Ou usando attempt para execução segura
  const handleSafeProcess = async (document) => {
    const result = await processDoc.attempt({ documentId: document.id });

    if (result.success) {
      console.log("Sucesso:", result.result);
    } else {
      console.error("Erro:", result.error);
    }
  };
}
```

## 📊 Estado e Tracking

### Estados Disponíveis

```tsx
const command = useCommand("my:command", { trackState: true });

// Estados booleanos
command.exists; // Comando existe no Commander
command.isAvailable; // Comando disponível (when() = true)
command.isLoading; // Execução em andamento

// Resultados e histórico
command.lastResult; // Último resultado bem-sucedido
command.lastError; // Último erro ocorrido
command.lastInput; // Último input utilizado
command.lastExecution; // Data da última execução
command.executionCount; // Número total de execuções

// Comando original
command.command; // Objeto Command completo
```

### Ciclo de Vida dos Estados

```tsx
// Estado inicial
isLoading: false
lastResult: null
lastError: null
executionCount: 0

// Durante execução
invoke() → isLoading: true

// Sucesso
→ isLoading: false
→ lastResult: [resultado]
→ lastError: null
→ executionCount: +1
→ lastExecution: new Date()

// Erro
→ isLoading: false
→ lastResult: [mantém anterior]
→ lastError: [erro]
→ executionCount: +1
→ lastExecution: new Date()
```

### Reset e Limpeza

```tsx
// Reset completo
command.reset(); // Limpa tudo, volta ao estado inicial

// Limpeza seletiva
command.clearError(); // Remove apenas lastError

// Refresh
command.refresh(); // Re-verifica isAvailable
```

## 🚨 Tratamento de Erros

### Estratégias de Erro

```tsx
// 1. Throw (padrão)
const command = useCommand("risky:operation");
try {
  await command();
} catch (error) {
  // Trata erro
}

// 2. Return null
const command = useCommand("risky:operation", {
  throwOnError: false,
});
const result = await command(); // null se erro

// 3. ExecutionResult
const command = useCommand("risky:operation", {
  returnInvoker: true,
});
const result = await command.attempt();
if (result.success) {
  // result.result
} else {
  // result.error
}
```

### Retry Automático

```tsx
const command = useCommand("unreliable:api", {
  retry: 3,
  retryDelay: (attempt) => {
    // Exponential backoff
    return Math.min(1000 * Math.pow(2, attempt), 10000);
  },

  onError: (error, input) => {
    console.log(`Tentativa falhou: ${error.message}`);
  },
});
```

## ⚡ Performance e Otimizações

### Debounce e Throttle

```tsx
// Debounce - aguarda pausa na entrada
const searchCommand = useCommand("search:execute", {
  debounce: 300, // Aguarda 300ms sem nova execução
  transformInput: (input) => ({
    ...input,
    timestamp: Date.now(),
  }),
});

// Throttle - limita frequência
const saveCommand = useCommand("auto:save", {
  throttle: 5000, // Máximo 1 save a cada 5s
  throwOnError: false,
});
```

### Memoização e Re-renders

```tsx
// ✅ Bom - não recria a cada render
const command = useCommand("stable:command", {
  trackState: true,
});

// ⚠️ Cuidado - recria options a cada render
const command = useCommand("unstable:command", {
  onSuccess: (result) => {
    // Nova função a cada render
    console.log(result);
  },
});

// ✅ Melhor - callback memoizado
const handleSuccess = useCallback((result) => {
  console.log(result);
}, []);

const command = useCommand("stable:command", {
  onSuccess: handleSuccess,
});
```

### Reset Inteligente

```tsx
const command = useCommand(dynamicKey, {
  trackState: true,
  resetOnKeyChange: true, // Reset automático quando key muda
});

// Equivale a:
useEffect(() => {
  command.reset();
}, [dynamicKey]);
```

## 🔍 Verificações e Validações

### Verificação de Disponibilidade

```tsx
const command = useCommand("conditional:command", {
  returnInvoker: true,
});

// Verificação manual
const canExecute = await command.canInvoke({ userId: 123 });
if (canExecute) {
  await command.invoke({ userId: 123 });
}

// Verificação automática (padrão)
// invoke() já verifica disponibilidade automaticamente
```

### Validação Personalizada

```tsx
const command = useCommand("user:create", {
  validateInput: (input) => {
    // Retorna true = válido
    // Retorna string = mensagem de erro
    // Retorna false = erro genérico

    if (!input?.email) return "Email é obrigatório";
    if (!input?.password || input.password.length < 8) {
      return "Senha deve ter pelo menos 8 caracteres";
    }
    return true;
  },

  throwOnError: false, // Para capturar erros de validação
});
```

## 📈 Casos de Uso Avançados

### 1. Command Chaining

```tsx
function DocumentWorkflow() {
  const processDoc = useCommand("document:process", { trackState: true });
  const generatePDF = useCommand("pdf:generate", { trackState: true });
  const sendEmail = useCommand("email:send", { trackState: true });

  const runWorkflow = async (document) => {
    try {
      // Processamento
      const processed = await processDoc.invoke({ document });

      // Geração PDF
      const pdf = await generatePDF.invoke({
        content: processed.content,
      });

      // Envio por email
      await sendEmail.invoke({
        to: document.author.email,
        attachment: pdf.url,
      });

      toast.success("Workflow concluído!");
    } catch (error) {
      toast.error(`Workflow falhou: ${error.message}`);
    }
  };

  const isWorkflowRunning =
    processDoc.isLoading || generatePDF.isLoading || sendEmail.isLoading;

  return (
    <div>
      <button
        onClick={() => runWorkflow(currentDocument)}
        disabled={isWorkflowRunning}
      >
        {isWorkflowRunning ? "Processando..." : "Executar Workflow"}
      </button>

      {/* Status de cada etapa */}
      <div className="mt-4 space-y-2">
        <WorkflowStep
          label="Processamento"
          status={getStepStatus(processDoc)}
        />
        <WorkflowStep label="Geração PDF" status={getStepStatus(generatePDF)} />
        <WorkflowStep label="Envio Email" status={getStepStatus(sendEmail)} />
      </div>
    </div>
  );
}
```

### 2. Conditional Execution

```tsx
function SmartSaveButton() {
  const autoSave = useCommand("document:auto-save", {
    trackState: true,
    throttle: 10000, // Auto-save máximo a cada 10s
    throwOnError: false,
  });

  const manualSave = useCommand("document:save", {
    trackState: true,
    onSuccess: () => toast.success("Documento salvo!"),
  });

  // Auto-save quando conteúdo muda
  useEffect(() => {
    if (documentContent && hasUnsavedChanges) {
      autoSave.invoke({ content: documentContent });
    }
  }, [documentContent]);

  // Save manual
  const handleManualSave = async () => {
    const result = await manualSave.invoke({
      content: documentContent,
      force: true, // Força save mesmo se auto-save recente
    });
  };

  const lastSaveTime = Math.max(
    autoSave.lastExecution?.getTime() || 0,
    manualSave.lastExecution?.getTime() || 0,
  );

  return (
    <div>
      <button onClick={handleManualSave} disabled={manualSave.isLoading}>
        {manualSave.isLoading ? "Salvando..." : "Salvar"}
      </button>

      <div className="text-sm text-gray-500">
        {lastSaveTime && (
          <span>Último save: {formatDistanceToNow(lastSaveTime)}</span>
        )}
        {autoSave.isLoading && (
          <span className="ml-2">• Auto-save em andamento</span>
        )}
      </div>
    </div>
  );
}
```

## 🎯 Boas Práticas

### 1. Tipagem Consistente

```tsx
// ✅ Defina interfaces claras
interface SaveDocumentInput {
  content: string;
  title: string;
  tags?: string[];
}

interface SaveDocumentOutput {
  id: string;
  url: string;
  savedAt: Date;
}

const saveDoc = useCommand<SaveDocumentInput, SaveDocumentOutput>("doc:save");
```

### 2. Error Boundaries

```tsx
function DocumentEditor() {
  const saveDoc = useCommand("doc:save", {
    onError: (error, input) => {
      // Log para monitoramento
      logger.error("Save failed", {
        error: error.message,
        documentId: input?.id,
      });

      // Fallback local
      localStorage.setItem("unsaved_doc", JSON.stringify(input));
    },
  });
}
```

### 3. Loading States Granulares

```tsx
function MultiStepForm() {
  const validateStep = useCommand("form:validate-step", { trackState: true });
  const submitForm = useCommand("form:submit", { trackState: true });

  const isValidating = validateStep.isLoading;
  const isSubmitting = submitForm.isLoading;
  const isProcessing = isValidating || isSubmitting;

  return (
    <form>
      {/* Campos do formulário */}

      <button
        type="button"
        onClick={() => validateStep.invoke({ step: currentStep })}
        disabled={isProcessing}
      >
        {isValidating ? "Validando..." : "Validar Etapa"}
      </button>

      <button
        type="submit"
        onClick={() => submitForm.invoke(formData)}
        disabled={isProcessing || !isFormValid}
      >
        {isSubmitting ? "Enviando..." : "Enviar"}
      </button>
    </form>
  );
}
```

O `useCommand` é o hook mais poderoso e flexível do Commander, oferecendo controle total sobre execução, estado e comportamento dos comandos.
