# 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.
