# CLI System - Framework OfficeGest

> **Nível**: Básico a Avançado  
> **Pré-requisitos**: Conhecimento básico de PHP e conceitos de linha de comandos  
> **Tempo de leitura**: ~15 minutos

---

## 📋 Índice

1. [Introdução](#introdução)
2. [Quick Start](#quick-start)
3. [Arquitetura e Estrutura](#arquitetura-e-estrutura)
4. [Criando Comandos](#criando-comandos)
5. [Sistema de Assinaturas](#sistema-de-assinaturas)
6. [Input e Output](#input-e-output)
7. [Validação de Argumentos](#validação-de-argumentos)
8. [Comandos Disponíveis](#comandos-disponíveis)
9. [Exemplos Práticos](#exemplos-práticos)
10. [Boas Práticas](#boas-práticas)
11. [Troubleshooting](#troubleshooting)
12. [Referência de API](#referência-de-api)

---

## Introdução

### O que é o CLI System?

O CLI (Command Line Interface) do OfficeGest é uma ferramenta de linha de comandos que permite executar tarefas de desenvolvimento, manutenção e gestão do ERP sem interface web.

### Por que usar o CLI?

| Via Web | Via CLI |
|---------|---------|
| Interface gráfica | Terminal/Consola |
| Requer browser | Automatizável em scripts |
| Timeout de 30s | Pode correr horas |
| Interação visual | Processamento em batch |

### Quando usar?

✅ **Use CLI para:**
- Gerar código (controllers, actions, etc.)
- Limpar caches
- Executar migrations
- Processar dados em batch
- Tarefas agendadas (cron)
- Deploy automatizado

❌ **Use Web para:**
- Interações com utilizadores finais
- Visualização de dados
- Operações que requerem UI

---

## Quick Start

### 🚀 Primeiros Passos

```bash
# 1. LISTAR todos os comandos disponíveis
./og list

# 2. OBTER ajuda sobre um comando
./og help make:controller

# 3. LIMPAR cache (comando mais comum)
./og cache:clear

# 4. GERAR um novo controller
./og make:controller MeuController --module=MeuModulo

# 5. EXECUTAR migrations
./og migrate
```

### Comandos Essenciais do Dia-a-Dia

```bash
# Desenvolvimento
./og make:controller NomeController --module=Modulo
./og make:action NomeAction --module=Modulo
./og make:migration create_table_name

# Manutenção
./og cache:clear          # Limpa todo o cache
./og route:cache:clear    # Limpa cache de rotas
./og config:cache:clear   # Limpa cache de configuração

# Base de dados
./og migrate              # Executa migrations pendentes
./og rollback             # Reverte última migration
```

### Com Contexto HTTP (Para comandos que precisam de BD)

```bash
# Definir HTTP_HOST para comandos que precisam de contexto
HTTP_HOST=meudominio.com ./og <comando>

# Exemplo:
HTTP_HOST=demo.officegest.com ./og migrate
```

---

## Arquitetura e Estrutura

### Estrutura de Ficheiros

```
Modules/Common/CLI/
├── CLIApplication.php      # Aplicação principal (auto-descoberta)
├── Command.php             # Classe base para todos os comandos
├── Parser.php              # Parser de assinaturas Laravel-style
├── IO.php                  # Trait para input/output
├── CallsCommands.php       # Trait para chamar outros comandos
├── FormatData.php          # Trait para formatação de dados
└── Commands/               # Comandos pré-definidos (26 comandos)
    ├── Make*.php           # Geradores de código
    ├── Cache*.php          # Gestão de cache
    ├── Route*.php          # Gestão de rotas
    └── Migrate*.php        # Migrations
```

### Diagrama de Componentes

```mermaid
graph TB
    A[Terminal ./og] --> B[og script PHP]
    B --> C[CLIApplication]
    C --> D[Auto-descoberta de Comandos]
    D --> E[Varrer Modules/**/Commands/]
    E --> F[Registar Comandos]
    
    G[Utilizador executa comando] --> H[CLIApplication::run]
    H --> I[Command::execute]
    I --> J[Validação de Input]
    J --> K{Válido?}
    K -->|Sim| L[handle ou __invoke]
    K -->|Não| M[Erro + Exit]
    L --> N[Output ao Utilizador]
```

### Como Funciona a Auto-Descoberta

O `CLIApplication.php` varre automaticamente todos os módulos:

```php
// CLIApplication.php:25-51
private function getCommands(): array
{
    $commands = [];
    $directory = rootPath('Modules');
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($directory)
    );

    foreach ($iterator as $file) {
        // Procura ficheiros que terminam em 'Command.php'
        if (!str_ends_with($file->getFilename(), 'Command.php')) {
            continue;
        }
        
        // Constrói o namespace automaticamente
        $fullClassName = 'Og\\Modules\\' . $relativePath;
        
        // Verifica se herda de Command
        if (is_subclass_of($fullClassName, Command::class)) {
            $commands[] = $fullClassName;
        }
    }
    return $commands;
}
```

**Resultado:** Qualquer classe `*Command.php` em `Modules/*/Commands/` é automaticamente disponibilizada!

---

## Criando Comandos

### Estrutura Básica de um Comando

```php
<?php

namespace Og\Modules\MeuModulo\Commands;

use Og\Modules\Common\CLI\Command;

class MeuCommand extends Command
{
    // Nome do comando (executado via ./og meu:comando)
    protected ?string $signature = 'meu:comando {argumento} {--opcao}';
    
    // Descrição exibida no ./og list
    protected string $description = 'Descrição do meu comando';

    // Lógica principal
    public function handle(): void
    {
        $argumento = $this->argument('argumento');
        $opcao = $this->option('opcao');
        
        $this->info("Executando com: {$argumento}");
        
        // Lógica aqui...
        
        $this->success('Comando executado com sucesso!');
    }
}
```

### Gerar Comando via CLI

```bash
# Usar o gerador de comandos
./og make:command ProcessarDados --module=MeuModulo

# Ficheiro criado em:
# Modules/MeuModulo/Commands/ProcessarDadosCommand.php
```

### Localização do Comando

O comando deve estar em:
```
Modules/{NomeDoModulo}/Commands/{NomeDoComando}Command.php
```

---

## Sistema de Assinaturas

### O que é uma Assinatura?

A assinatura define o **nome**, **argumentos** e **opções** do comando numa única string:

```php
protected ?string $signature = 'nome:comando {arg} {--opt}';
```

### Sintaxe Completa

```php
// NOME DO COMANDO
'make:controller'           // Executado via: ./og make:controller

// ARGUMENTOS
'{name}'                    // Obrigatório
'{name?}'                   // Opcional
'{name=default}'            // Com valor default
'{name*}'                   // Array (múltiplos valores)
'{name?*}'                  // Array opcional
'{name : Descrição}'        // Com descrição

// OPÇÕES
'{--force}'                 // Flag booleana (true/false)
'{--name=}'                 // Opção com valor obrigatório
'{--name=default}'          // Opção com valor default
'{--name=*}'                // Opção array
'{-f|--force}'              // Atalho (-f é o mesmo que --force)
'{--format= : Formato}'     // Com descrição
```

### Exemplos Práticos de Assinaturas

```php
// Comando simples
'cache:clear'

// Com argumento obrigatório
'make:controller {name}'

// Com argumento e opção
'make:action {name} {--module=}'

// Comando complexo
'import:data {file} {--format=csv} {--batch=100} {--dry-run} {--force}'

// Com descrições (aparece no --help)
'process:items 
    {type : Tipo de item a processar}
    {--limit=100 : Limite de itens}
    {--dry-run : Apenas simular}'
```

---

## Input e Output

### Métodos de Output (Trait IO)

```php
// MENSAGENS COLORIDAS
$this->info('Informação...');      // Azul
$this->success('Sucesso!');        // Verde
$this->warning('Atenção...');      // Amarelo
$this->error('Erro!');             // Vermelho

// ORGANIZAÇÃO VISUAL
$this->title('Título Principal');
$this->section('Secção');
$this->newLine();                  // Linha em branco
$this->newLine(3);                 // 3 linhas em branco

// LISTAS
$this->listing([
    'Item 1',
    'Item 2',
    'Item 3',
]);

// TABELAS
$this->table(
    ['Nome', 'Email', 'Status'],   // Headers
    [
        ['João', 'joao@email.com', 'Ativo'],
        ['Maria', 'maria@email.com', 'Inativo'],
    ]
);

// BARRA DE PROGRESSO
$bar = $this->progressBar(100);
for ($i = 0; $i < 100; $i++) {
    // Processar...
    $bar->advance();
}
$bar->finish();
$this->newLine();
```

### Métodos de Input

```php
// OBTER ARGUMENTOS
$name = $this->argument('name');           // Argumento específico
$allArgs = $this->arguments();             // Todos os argumentos

// OBTER OPÇÕES
$format = $this->option('format');         // Opção específica
$allOpts = $this->options();               // Todas as opções

// PERGUNTAS AO UTILIZADOR
$name = $this->ask('Qual o seu nome?');
$name = $this->ask('Nome?', 'Default');    // Com default

// CONFIRMAÇÃO
if ($this->confirm('Tem a certeza?', true)) {
    // Utilizador disse sim
}

// SENHA (não mostra input)
$password = $this->secret('Password:');

// ESCOLHA
$choice = $this->choice(
    'Escolha um formato',
    ['csv', 'json', 'xml'],
    'csv'  // default
);
```

### Formatação de Dados (Trait FormatData)

```php
// Formatar bytes
$this->formatBytes(1024);           // "1 KB"
$this->formatBytes(1048576);        // "1 MB"
$this->formatBytes(1073741824);     // "1 GB"

// Formatar delta (diferença)
$this->formatDelta(100);            // "+100"
$this->formatDelta(-50);            // "-50"
```

---

## Validação de Argumentos

### Regras de Validação Simples

```php
class MeuCommand extends Command
{
    protected ?string $signature = 'processar {type} {--count=10}';
    
    protected array $validationRules = [
        'type'  => 'required',
        'count' => ['integer', 'positive'],
    ];
    
    public function handle(): void
    {
        // Se validação falhar, handle() não é executado
        // e mensagem de erro é exibida automaticamente
    }
}
```

### Regras Disponíveis

```php
$validationRules = [
    'campo1' => 'required',              // Obrigatório
    'campo2' => 'integer',               // Número inteiro
    'campo3' => 'positive',              // Número positivo
    'campo4' => 'array',                 // Deve ser array
    'campo5' => ['required', 'integer'], // Múltiplas regras
];
```

### Validação Customizada

```php
protected array $validationRules = [
    'email' => function($value) {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            return 'Email inválido';  // Mensagem de erro
        }
        return true;  // Válido
    },
    
    'age' => fn($value) => $value >= 18 ? true : 'Deve ter 18+ anos',
];
```

### Usando Sistema de Validação do Framework

```php
public function handle(): void
{
    // Usar o sistema de validação completo do OfficeGest
    $data = [
        'file' => $this->argument('file'),
        'format' => $this->option('format'),
    ];
    
    if (!validate($data, [
        'file' => ['required', 'string'],
        'format' => ['required', 'in:csv,json,xml'],
    ])) {
        foreach (validationErrors() as $field => $errors) {
            $this->error("{$field}: " . implode(', ', $errors));
        }
        return;
    }
    
    $validated = validated();
    $this->info("Processando: {$validated['file']}");
}
```

---

## Comandos Disponíveis

### 🔧 Geradores de Código (Make Commands)

| Comando | Descrição | Exemplo |
|---------|-----------|---------|
| `make:module` | Cria módulo completo | `./og make:module Billing --all` |
| `make:controller` | Cria controller | `./og make:controller UserController --module=Users` |
| `make:action` | Cria action | `./og make:action CreateUser --module=Users` |
| `make:dto` | Cria DTO | `./og make:dto UserData --module=Users` |
| `make:policy` | Cria policy | `./og make:policy UserPolicy --module=Users` |
| `make:request` | Cria request | `./og make:request CreateUserRequest --module=Users` |
| `make:route` | Cria ficheiro de rotas | `./og make:route api --module=Users` |
| `make:middleware` | Cria middleware | `./og make:middleware AuthMiddleware` |
| `make:migration` | Cria migration | `./og make:migration create_users_table` |
| `make:command` | Cria comando CLI | `./og make:command ProcessData --module=Batch` |

### 🗑️ Gestão de Cache

| Comando | Descrição | Uso |
|---------|-----------|-----|
| `cache:clear` | Limpa todo o cache | `./og cache:clear` |
| `config:cache` | Gera cache de configuração | `./og config:cache` |
| `config:cache:clear` | Limpa cache de config | `./og config:cache:clear` |
| `view:cache:clear` | Limpa cache de views | `./og view:cache:clear` |

### 🛣️ Gestão de Rotas

| Comando | Descrição | Uso |
|---------|-----------|-----|
| `route:list` | Lista todas as rotas | `./og route:list` |
| `route:cache` | Gera cache de rotas | `./og route:cache` |
| `route:cache:clear` | Limpa cache de rotas | `./og route:cache:clear` |
| `route:cache:test` | Testa cache de rotas | `./og route:cache:test` |

### 🗄️ Base de Dados

| Comando | Descrição | Uso |
|---------|-----------|-----|
| `migrate` | Executa migrations | `./og migrate` |
| `rollback` | Reverte última migration | `./og rollback` |

### 🔑 Utilitários

| Comando | Descrição | Uso |
|---------|-----------|-----|
| `key:generate` | Gera chave de aplicação | `./og key:generate` |
| `queue:test:locks` | Testa sistema de locks | `./og queue:test:locks` |

---

## Exemplos Práticos

### 🎯 Exemplo 1: Comando de Processamento em Batch

```php
namespace Og\Modules\Batch\Commands;

use Og\Modules\Common\CLI\Command;

class ProcessOrdersCommand extends Command
{
    protected ?string $signature = 'orders:process 
        {--status=pending : Status dos pedidos a processar}
        {--batch=100 : Tamanho do batch}
        {--dry-run : Apenas simular}';
    
    protected string $description = 'Processa pedidos em batch';

    public function handle(): void
    {
        $status = $this->option('status');
        $batchSize = (int) $this->option('batch');
        $dryRun = $this->option('dry-run');
        
        if ($dryRun) {
            $this->warning('🔍 Modo DRY-RUN ativado');
        }
        
        $this->title("Processando Pedidos [{$status}]");
        
        $orders = Order::where('status', $status)->get();
        $total = count($orders);
        
        if ($total === 0) {
            $this->info('Nenhum pedido encontrado.');
            return;
        }
        
        $this->info("Encontrados {$total} pedidos");
        $bar = $this->progressBar($total);
        
        $processed = 0;
        $errors = 0;
        
        foreach ($orders as $order) {
            try {
                if (!$dryRun) {
                    $this->processOrder($order);
                }
                $processed++;
            } catch (\Exception $e) {
                $errors++;
                $this->newLine();
                $this->error("Erro no pedido #{$order->id}: {$e->getMessage()}");
            }
            $bar->advance();
        }
        
        $bar->finish();
        $this->newLine(2);
        
        $this->table(
            ['Métrica', 'Valor'],
            [
                ['Total', $total],
                ['Processados', $processed],
                ['Erros', $errors],
            ]
        );
        
        $this->success('Processamento concluído!');
    }
}
```

### 🎯 Exemplo 2: Comando de Deploy

```php
namespace Og\Modules\DevOps\Commands;

use Og\Modules\Common\CLI\Command;

class DeployCommand extends Command
{
    protected ?string $signature = 'deploy {--force : Forçar deploy}';
    protected string $description = 'Deploy da aplicação';

    public function handle(): void
    {
        $this->title('🚀 Deploy da Aplicação');
        
        if (!$this->option('force')) {
            if (!$this->confirm('Tem a certeza que deseja fazer deploy?')) {
                $this->warning('Deploy cancelado.');
                return;
            }
        }
        
        // 1. Limpar caches
        $this->section('Limpando Caches');
        $this->call('cache:clear');
        $this->call('config:cache:clear');
        $this->call('route:cache:clear');
        $this->call('view:cache:clear');
        
        // 2. Executar migrations
        $this->section('Executando Migrations');
        $this->call('migrate', ['--force' => true]);
        
        // 3. Reconstruir caches
        $this->section('Reconstruindo Caches');
        $this->call('config:cache');
        $this->call('route:cache');
        
        $this->newLine();
        $this->success('✅ Deploy concluído com sucesso!');
    }
}
```

### 🎯 Exemplo 3: Comando Interativo

```php
namespace Og\Modules\Setup\Commands;

use Og\Modules\Common\CLI\Command;

class SetupWizardCommand extends Command
{
    protected ?string $signature = 'setup:wizard';
    protected string $description = 'Wizard de configuração inicial';

    public function handle(): void
    {
        $this->title('🧙 Wizard de Configuração');
        
        // Perguntas interativas
        $companyName = $this->ask('Nome da empresa?');
        $adminEmail = $this->ask('Email do administrador?');
        $timezone = $this->choice(
            'Timezone?',
            ['Europe/Lisbon', 'Europe/London', 'America/Sao_Paulo'],
            'Europe/Lisbon'
        );
        
        // Confirmar
        $this->newLine();
        $this->info('Configuração:');
        $this->listing([
            "Empresa: {$companyName}",
            "Email: {$adminEmail}",
            "Timezone: {$timezone}",
        ]);
        
        if (!$this->confirm('Confirmar configuração?')) {
            $this->warning('Configuração cancelada.');
            return;
        }
        
        // Aplicar
        $bar = $this->progressBar(3);
        
        $this->saveConfig('company.name', $companyName);
        $bar->advance();
        
        $this->createAdmin($adminEmail);
        $bar->advance();
        
        $this->setTimezone($timezone);
        $bar->advance();
        
        $bar->finish();
        $this->newLine();
        $this->success('Setup concluído!');
    }
}
```

---

## Boas Práticas

### ✅ Nomes de Comandos

```php
// BOM - namespace:ação
'cache:clear'
'make:controller'
'users:sync'
'orders:process'

// MAU - sem namespace ou confuso
'clearcache'
'makeController'
'do-something'
```

### ✅ Descrições Claras

```php
// BOM
protected string $description = 'Processa pedidos pendentes e envia notificações';

// MAU
protected string $description = 'Comando de pedidos';
```

### ✅ Output Informativo

```php
// BOM - feedback ao utilizador
$this->info("Processando {$total} registos...");
$bar = $this->progressBar($total);
// ...
$this->success("Concluído! {$processed} processados, {$errors} erros.");

// MAU - silêncio total
foreach ($items as $item) {
    $this->process($item);
}
// Acabou sem dizer nada...
```

### ✅ Tratamento de Erros

```php
public function handle(): void
{
    try {
        $this->riskyOperation();
    } catch (SpecificException $e) {
        $this->error("Erro específico: {$e->getMessage()}");
        return;
    } catch (\Exception $e) {
        $this->error("Erro inesperado: {$e->getMessage()}");
        loggerBatch('command_error', ['error' => $e->getMessage()]);
        return;
    }
}
```

### ✅ Dry-Run para Operações Destrutivas

```php
protected ?string $signature = 'cleanup:old-data {--dry-run}';

public function handle(): void
{
    $items = $this->getOldItems();
    
    if ($this->option('dry-run')) {
        $this->warning('DRY-RUN: Seriam eliminados ' . count($items) . ' itens');
        $this->table(['ID', 'Criado em'], $items->map(...)->toArray());
        return;
    }
    
    if (!$this->confirm('Eliminar ' . count($items) . ' itens?')) {
        return;
    }
    
    // Eliminar de verdade...
}
```

---

## Troubleshooting

### Problema: Comando não é encontrado

```bash
$ ./og meu:comando
Command "meu:comando" is not defined.
```

**Soluções:**
1. Verificar se o ficheiro termina em `Command.php`
2. Verificar se está na pasta `Commands` de um módulo
3. Verificar se a classe herda de `Command`
4. Verificar namespace correcto

```php
// Certo:
namespace Og\Modules\MeuModulo\Commands;
class MeuCommand extends Command  // ✅

// Errado:
namespace Og\Modules\MeuModulo;    // ❌ Falta \Commands
class Meu extends Command         // ❌ Falta "Command" no nome
```

### Problema: HTTP_HOST is not defined

```bash
$ ./og migrate
Exception: HTTP_HOST is not defined
```

**Solução:**
```bash
HTTP_HOST=meudominio.com ./og migrate
```

### Problema: Comando trava sem output

**Causa:** Output buffer ou processo pesado sem feedback.

**Solução:**
```php
// Usar progress bar para operações longas
$bar = $this->progressBar(count($items));
foreach ($items as $item) {
    $this->process($item);
    $bar->advance();
}
$bar->finish();
```

### Debug de Comandos

```bash
# Modo verbose
./og meu:comando -v

# Modo muito verbose
./og meu:comando -vv

# Modo debug
./og meu:comando -vvv
```

---

## Referência de API

### Command (Classe Base)

```php
class Command extends SymfonyCommand
{
    // Propriedades
    protected ?string $signature = null;
    protected string $description = '';
    protected array $validationRules = [];
    
    // Método principal (implementar este)
    public function handle(): void;
    
    // Input
    public function argument(string $name): mixed;
    public function arguments(): array;
    public function option(string $name): mixed;
    public function options(): array;
    
    // Output
    public function info(string $message): void;
    public function error(string $message): void;
    public function warning(string $message): void;
    public function success(string $message): void;
    public function table(array $headers, array $rows): void;
    public function progressBar(int $max): ProgressBar;
    
    // Interação
    public function ask(string $question, ?string $default = null): string;
    public function confirm(string $question, bool $default = true): bool;
    public function choice(string $question, array $choices, $default): string;
    
    // Chamar outros comandos
    public function call(string $command, array $arguments = []): int;
    public function callSilently(string $command, array $arguments = []): int;
}
```

### Parser (Sintaxe de Assinatura)

```
{name}              # Argumento obrigatório
{name?}             # Argumento opcional
{name=default}      # Com valor default
{name*}             # Array
{name : Descrição}  # Com descrição

{--option}          # Flag booleana
{--option=}         # Opção com valor
{--option=default}  # Com valor default
{--o|option}        # Com atalho
```

---

## Próximos Passos

- 📖 [Queue System](./queue-system.md) - Jobs em background
- 📖 [Container DI](./container-system.md) - Injecção de dependências
- 📖 [Validation System](./validation-system.md) - Sistema de validação

---

*Última atualização: Dezembro 2024*
