# OfficeGest Framework

**Framework moderno para OfficeGest ERP - Documentação Completa**

---

## Índice

1. [Visão Geral](#visão-geral)
2. [Arquitetura](#arquitetura)
3. [Container & Dependency Injection](#container--dependency-injection)
4. [Routing System](#routing-system)
5. [HTTP Layer](#http-layer)
6. [Queue System](#queue-system)
7. [Validation Framework](#validation-framework)
8. [View Engine](#view-engine)
9. [Legacy Integration](#legacy-integration)
10. [Exemplos Práticos](#exemplos-práticos)
11. [Anti-padrões e Boas Práticas](#anti-padrões-e-boas-práticas)
12. [Convenções](#convenções)

---

## Visão Geral

O Framework OfficeGest é uma camada moderna construída sobre o sistema legacy existente, fornecendo:

- **Dependency Injection Container** baseado no Laravel
- **Queue System** robusto com múltiplos drivers (Redis, RabbitMQ, Database)
- **Validation Framework** poderoso e extensível
- **HTTP Layer** com middlewares e response helpers
- **View Engine** usando Blade templates
- **Routing System** moderno com controllers organizados
- **Legacy Integration** transparente com o código existente

### Filosofia

O framework visa:
- **Simplicidade**: APIs intuitivas e fáceis de aprender
- **Desempenho**: Código optimizado para alto desempenho
- **Flexibilidade**: Extensível sem quebrar funcionalidades existentes
- **Compatibilidade**: Integração transparente com código legado

---

## Arquitetura

### Estrutura de Diretórios

```
Modules/Common/
├── Container/          # Dependency Injection
├── Queue/             # Sistema de filas
├── Validation/        # Framework de validação
├── Http/             # Layer HTTP (Request, Response, Middleware)
├── View/             # Engine de views (Blade)
├── Routing/          # Sistema de rotas
├── Legacy/           # Integração com sistema antigo
├── Utils/            # Utilitários diversos
└── Config/           # Arquivos de configuração
```

### Processo de Bootstrap

O framework inicializa em duas etapas:

1. **init_1()**: Segurança, configuração inicial, carregamento de constantes
2. **init_2()**: Base de dados, módulos, sessão, variáveis globais

```php
// Em bootstrap.php
$container = Container::getInstance();
$container->boot();

// Container disponível globalmente através do app()
$queueManager = app(QueueManager::class);
```

---

## Container & Dependency Injection

### O que é e Por que Usar?

O **Container** é o coração do framework - um "recipiente inteligente" que gerencia todas as dependências da aplicação. Em vez de criar objetos manualmente com `new`, o Container os cria e injeta automaticamente onde necessário.

**Problema que resolve:**
- ❌ **Sem Container**: `$service = new EmailService(new SmtpTransport(), new Logger());` - cada sítio precisa de saber como criar
- ✅ **Com Container**: `$service = app(EmailService::class);` - o Container sabe como criar e injeta automaticamente

**Componentes principais:**

1. **Service Providers**: "Receitas" que ensinam o Container como criar cada serviço
2. **Singleton registration**: Garante que alguns serviços tenham apenas uma instância (como database)
3. **Auto-wiring**: Container examina construtores e injeta dependências automaticamente
4. **Method injection**: Nos controllers, parâmetros são injetados automaticamente

### Como Funciona na Prática

Quando você pede um serviço, o Container:
1. Verifica se já tem uma instância (se for singleton)
2. Examina o construtor da classe
3. Resolve todas as dependências recursivamente
4. Cria o objeto com todas as dependências
5. Retorna pronto para uso

### Registrando Serviços

```php
// Em um ServiceProvider
public function register(): void
{
    $this->container->bind('my-service', MyService::class);

    $this->container->singleton('database', function() {
        return new DatabaseManager(config('database'));
    });

    $this->container->alias('database', DatabaseManager::class);
}
```

### Usando o Container

```php
// Resolução básica
$service = app('my-service');
$service = app(MyService::class);

// Com parâmetros
$service = app()->make(MyService::class, ['param' => 'value']);

// Em controllers (auto-wiring)
class UserController
{
    public function __construct(
        private UserService $userService,
        private QueueManager $queue
    ) {}

    public function store(Request $request, ValidationService $validator)
    {
        // Parâmetros injetados automaticamente
    }
}
```

### Service Providers

```php
<?php
namespace Og\Modules\MyModule;

use Og\Modules\Common\Providers\ServiceProviderInterface;

class MyModuleServiceProvider implements ServiceProviderInterface
{
    public function register(): void
    {
        $this->container->singleton(MyService::class, function() {
            return new MyService(config('my-module.settings'));
        });
    }

    public function boot(): void
    {
        // Código que roda após todos os providers estarem registrados
        if ($this->container->bound('queue')) {
            app('queue')->addConnection('my-custom-queue', [...]);
        }
    }
}
```

**Anti-padrão:**
```php
// ❌ NÃO faça isso - Hard-coded dependency
$service = new MyService();
// PROBLEMA: Impossível testar, não permite injeção de mocks,
// dificulta troca de implementação, cria acoplamento forte

// ❌ Não use o container como Service Locator
class BadController
{
    public function store()
    {
        $service = app(UserService::class); // Service Locator pattern
        // PROBLEMA: Esconde dependências, torna classe difícil de testar,
        // quebra princípio da inversão de dependência, cria acoplamento
        // com o container dentro da lógica de negócio
    }
}
```

---

## Routing System

### Definindo Rotas

```php
// Em Modules/MyModule/Routes/web.php
<?php

use Og\Modules\Common\Routing\Router;
use Og\Modules\MyModule\Controllers\UserController;

Router::group(function() {
    Router::prefix('api/v1')->group(function() {
        Router::get('/users', [UserController::class, 'index']);
        Router::post('/users', [UserController::class, 'store']);
        Router::get('/users/{id}', [UserController::class, 'show']);
        Router::patch('/users/{id}', [UserController::class, 'update']);
        Router::delete('/users/{id}', [UserController::class, 'destroy']);
    });

    // Com middleware
    Router::middleware(['auth'])->group(function() {
        Router::get('/dashboard', [DashboardController::class, 'index']);
    });

    // Rotas nomeadas
    Router::get('/profile', [UserController::class, 'profile'])->name('profile');
});
```

### Controllers

```php
<?php
namespace Og\Modules\MyModule\Controllers;

use Og\Modules\Common\Http\Request;
use Og\Modules\Common\Http\Response;

class UserController
{
    public function __construct(
        private UserService $userService,
        private Response $response
    ) {}

    public function index(Request $request): Response
    {
        $users = $this->userService->paginate(
            page: $request->get('page', 1),
            limit: $request->get('limit', 15)
        );

        return $this->response->json(['data' => $users]);
    }

    public function store(StoreUserRequest $request): Response
    {
        $user = $this->userService->create($request->validated());

        return $this->response->json($user, 201);
    }

    public function show(Request $request, int $id): Response
    {
        $user = $this->userService->find($id);

        if (!$user) {
            return $this->response->json(['error' => 'User not found'], 404);
        }

        return $this->response->json($user);
    }
}
```

**Anti-padrão:**
```php
// ❌ Controller muito pesado (God Controller)
class BadUserController
{
    public function store(Request $request)
    {
        // ❌ Validação inline (deveria ser um FormRequest)
        if (empty($request->name)) {
            return ['error' => 'Name required'];
        }
        // PROBLEMA: Validação espalhada pelo código, difícil de reutilizar,
        // não padronizada, mistura responsabilidades

        // ❌ Lógica de negócio no controller (deveria ser um Service)
        $user = new User();
        $user->name = $request->name;
        $user->email = $request->email;
        // PROBLEMA: Controller fica inchado, lógica não reutilizável,
        // dificulta testes unitários da lógica de negócio

        // ❌ Acesso direto ao database (deveria usar Repository/Service)
        global $db;
        $db->insert('users', $user->toArray());
        // PROBLEMA: Acoplamento forte com database, impossível mockar,
        // quebra abstração, dificulta troca de storage

        return $user;
    }
}
```

---

## HTTP Layer

### O que é o HTTP Layer?

O **HTTP Layer** é a ponte entre o mundo externo (browsers, apps, APIs) e a sua aplicação. É composto por três peças principais que trabalham juntas:

1. **Request**: Representa tudo que vem do cliente (dados, headers, ficheiros)
2. **Response**: Representa tudo que volta para o cliente (JSON, HTML, redirects)
3. **Middleware**: "Filtros" que processam requests antes de chegar ao controller

**Fluxo típico:**
```
Browser → Middleware → Controller → Response → Browser
        ↑                      ↑
   (Auth, Rate Limit)    (Business Logic)
```

**Request - "O que o cliente quer"**

O objecto Request encapsula tudo que vem do cliente: dados do formulário, parâmetros da URL, headers, ficheiros enviados, informações sobre quem está a fazer a requisição.

### Request

```php
public function store(Request $request): Response
{
    // Acessando dados
    $name = $request->get('name');
    $email = $request->get('email', 'default@email.com'); // com default

    // Todos os dados
    $data = $request->all();

    // Apenas campos específicos
    $userData = $request->only(['name', 'email', 'phone']);

    // Excluindo campos
    $safeData = $request->except(['password_confirmation']);

    // Verificando se existe
    if ($request->has('avatar')) {
        // Campo avatar foi enviado
    }

    // Headers
    $contentType = $request->header('Content-Type');

    // Files
    if ($request->hasFile('avatar')) {
        $file = $request->file('avatar');
        $path = $file->store('avatars');
    }
}
```

### Response - "O que mandamos de volta"

O objeto Response é como você "responde" ao cliente. Pode ser JSON para APIs, HTML para páginas web, redirects, downloads de arquivos, etc. É a sua forma de comunicar o resultado da operação.

**Tipos comuns de resposta:**
- **JSON**: Para APIs (dados estruturados)
- **HTML**: Para páginas web (através de views)
- **Redirect**: Para enviar usuário para outra página
- **File**: Para download de arquivos
- **Empty**: Para operações que não retornam dados

### Response

```php
public function apiResponse(): Response
{
    // JSON Response
    return $this->response->json(['message' => 'Success']);

    // JSON com status específico
    return $this->response->json(['error' => 'Not found'], 404);

    // Redirect
    return $this->response->redirect('/dashboard');

    // Text
    return $this->response->text('Plain text response');

    // XML
    return $this->response->xml('<root><message>Hello</message></root>');

    // Headers personalizados
    return $this->response
        ->header('X-Custom', 'value')
        ->json(['data' => $data]);

    // No Content
    return $this->response->noContent();
}
```

### Middleware - "Filtros Inteligentes"

**Middleware** são como "porteiros" que examinam cada request antes dele chegar ao controller. Podem fazer verificações, modificar dados, bloquear acessos ou registrar logs.

**Casos de uso comuns:**
- 🔐 **Autenticação**: Só usuários logados passam
- ⏱️ **Rate Limiting**: Máximo de requests por minuto
- 📝 **Logging**: Registra todas as chamadas de API
- 🛡️ **CORS**: Permite requests de outros domínios
- 🔄 **Cache**: Retorna resposta em cache se disponível

**Como funciona:**
1. Request chega ao middleware
2. Middleware faz suas verificações/alterações
3. Chama `$next($request)` para continuar a cadeia
4. Ou retorna uma resposta (bloqueando o request)

### Middleware

```php
<?php
namespace Og\Modules\MyModule\Middleware;

use Og\Modules\Common\Http\Request;
use Og\Modules\Common\Http\Response;

class RateLimitMiddleware
{
    public function handle(Request $request, callable $next): Response
    {
        $key = 'rate_limit:' . $request->ip();
        $limit = 60; // 60 requests per minute

        $current = cache()->get($key, 0);

        if ($current >= $limit) {
            return app(Response::class)->json([
                'error' => 'Rate limit exceeded'
            ], 429);
        }

        cache()->put($key, $current + 1, 60);

        return $next($request);
    }
}

// Registrando middleware
Router::middleware([RateLimitMiddleware::class])->group(function() {
    // Suas rotas aqui
});
```

---

## Queue System

### O que é e Quando Usar?

O **Queue System** (Sistema de Filas) é um mecanismo para executar tarefas pesadas em segundo plano, sem fazer o usuário esperar. É como uma "lista de afazeres" que vai sendo processada aos poucos.

**Cenários típicos:**
- 📧 **Emails**: Enviar 1000 emails pode demorar 10 minutos
- 📊 **Relatórios**: Gerar relatório com 100k registros
- 🖼️ **Imagens**: Processar upload e redimensionar fotos
- 🧹 **Limpeza**: Deletar arquivos antigos, limpar cache

**Como funciona:**
1. **Job Creation**: Você cria uma classe que representa a tarefa
2. **Queue Dispatch**: Coloca a tarefa na fila com `$queue->push($job)`
3. **Worker Processing**: Um processo separado pega tarefas da fila e executa
4. **Result Handling**: Pode definir o que fazer em caso de sucesso/erro

**Benefícios:**
- ⚡ **Desempenho**: Utilizador não espera operações lentas
- 🔄 **Fiabilidade**: Se falhar, pode tentar novamente
- 📈 **Escalabilidade**: Pode ter múltiplos workers a processar
- 📊 **Monitorização**: Vê quantas tarefas estão pendentes/falharam

### Configuração

```php
// Config/queue.php
return [
    'default' => 'redis',

    'drivers' => [
        'redis' => [
            'host' => '127.0.0.1',
            'port' => 6379,
            'database' => 0,
        ],
        'rabbitmq' => [
            'host' => 'localhost',
            'port' => 5672,
            'username' => 'guest',
            'password' => 'guest',
        ]
    ]
];
```

### Criando Jobs

```php
<?php
namespace Og\Modules\MyModule\Jobs;

use Og\Modules\Common\Queue\Contracts\JobInterface;

class SendEmailJob implements JobInterface
{
    public function __construct(
        private string $to,
        private string $subject,
        private string $body
    ) {}

    public function handle(): void
    {
        // Sua lógica de envio aqui
        $emailService = app(EmailService::class);
        $emailService->send($this->to, $this->subject, $this->body);

        loggerBatch('info', 'Email sent', [
            'to' => $this->to,
            'subject' => $this->subject
        ]);
    }

    public function failed(\Throwable $exception): void
    {
        loggerBatch('error', 'Failed to send email', [
            'to' => $this->to,
            'error' => $exception->getMessage()
        ]);
    }

    public function getQueue(): ?string
    {
        return 'emails';
    }

    public function getDelay(): int
    {
        return 0;
    }

    public function getTries(): int
    {
        return 3;
    }
}
```

### Despachando Jobs

```php
public function sendWelcomeEmail(User $user): Response
{
    // Despacho imediato
    $queue = app(QueueManager::class);
    $jobId = $queue->push(new SendEmailJob(
        to: $user->email,
        subject: 'Welcome!',
        body: "Welcome {$user->name}!"
    ));

    // Despacho com delay
    $queue->later(
        delay: new \DateTime('+1 hour'),
        job: new SendReminderEmailJob($user->id)
    );

    // Com prioridade
    $queue->pushWithPriority(
        job: new UrgentNotificationJob($user->id),
        priority: 10
    );

    return $this->response->json(['job_id' => $jobId]);
}
```

### Batch Processing

```php
public function processBulkEmails(array $users): void
{
    $jobs = [];
    foreach ($users as $user) {
        $jobs[] = new SendEmailJob($user->email, 'Bulk message', 'Content...');
    }

    $queue = app(QueueManager::class);
    $batch = $queue->batch($jobs)
        ->name('bulk-email-campaign')
        ->onSuccess(function() {
            loggerBatch('info', 'Bulk email campaign completed');
        })
        ->onFailure(function() {
            loggerBatch('error', 'Bulk email campaign failed');
        })
        ->dispatch();
}
```

### Chain Processing

```php
public function processUserRegistration(array $userData): void
{
    $queue = app(QueueManager::class);

    $queue->chain([
        new CreateUserJob($userData),
        new SendWelcomeEmailJob($userData['email']),
        new SetupUserPreferencesJob($userData['preferences']),
        new NotifyAdminJob('New user registered')
    ])->dispatch();
}
```

**Anti-padrão:**
```php
// ❌ Processing pesado no request (Blocking Operations)
public function sendBulkEmails(Request $request)
{
    $users = User::all(); // ❌ Pode ser milhares - consome memória
    // PROBLEMA: Carrega todos os registros na memória de uma vez,
    // pode causar out-of-memory em datasets grandes

    foreach ($users as $user) {
        // ❌ Enviar email diretamente - vai dar timeout
        mail($user->email, 'Subject', 'Body');
        // PROBLEMA: Operação bloqueante que pode demorar minutos/horas,
        // utilizador fica à espera, request vai dar timeout,
        // se falhar no meio, perde todo o progresso
    }

    return 'Emails sent'; // ❌ Nunca vai chegar aqui devido ao timeout
    // PROBLEMA: Utilizador nunca recebe resposta, não sabe se funcionou
}
```

---

## Validation Framework

### O que é e Por que é Importante?

O **Validation Framework** é o "porteiro" da aplicação - garante que todos os dados que entram estejam corretos antes de serem processados. É como ter um checklist rigoroso para cada formulário.

**Por que validar:**
- 🛡️ **Segurança**: Impede SQL injection, XSS e outros ataques
- 🎯 **Consistência**: Dados sempre no formato esperado
- 🔧 **Manutenibilidade**: Regras centralizadas, fácil de alterar
- 👤 **UX**: Feedback claro para o utilizador sobre erros

**Tipos de validação:**
1. **Formato**: Email válido, NIF correcto, telefone no padrão
2. **Obrigatoriedade**: Campos required, required_if, sometimes
3. **Tamanho**: min/max length, tamanho do ficheiro
4. **Relacionamento**: exists (FK válida), unique (não duplicado)
5. **Negócio**: Regras específicas da empresa/módulo

**Como funciona:**
1. Define regras em FormRequest ou manualmente
2. Framework aplica cada regra aos dados recebidos
3. Se alguma falhar, devolve erros detalhados
4. Se todas passarem, devolve dados "limpos" e validados

### Validação Básica

```php
<?php
namespace Og\Modules\MyModule\Requests;

use Og\Modules\Common\Http\BaseRequest;

class StoreUserRequest extends BaseRequest
{
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'age' => 'required|integer|min:18|max:120',
            'avatar' => 'sometimes|file|image|max:2048',
            'preferences' => 'array',
            'preferences.newsletter' => 'boolean',
            'addresses' => 'array|min:1',
            'addresses.*.street' => 'required|string',
            'addresses.*.city' => 'required|string',
            'addresses.*.zipcode' => 'required|string|regex:/^\d{4}-\d{3}$/',
        ];
    }

    public function messages(): array
    {
        return [
            'name.required' => 'O nome é obrigatório',
            'email.unique' => 'Este email já está em uso',
            'addresses.*.zipcode.regex' => 'Código postal deve estar no formato 0000-000',
        ];
    }

    public function authorize(): bool
    {
        // Verificar se utilizador tem permissão para criar utilizadores
        global $u;
        return $u->checkPriv('users:create');
    }
}
```

### Validação Manual

```php
public function validateData(array $data): array
{
    $validator = app(Validator::class);

    $validator->setRules([
        'product_name' => 'required|string|max:100',
        'price' => 'required|numeric|min:0',
        'category_id' => 'required|integer|exists:categories,id',
        'tags' => 'array|max:10',
        'tags.*' => 'string|max:50'
    ])->withRequest($data);

    if (!$validator->validate()) {
        throw new ValidationException($validator->errors());
    }

    return $validator->validated();
}
```

### Custom Rules

```php
<?php
namespace Og\Modules\MyModule\Rules;

use Og\Modules\Common\Validation\Contracts\RuleInterface;

class UniqueInCompany implements RuleInterface
{
    public function __construct(
        private string $table,
        private string $column,
        private ?int $ignoreId = null
    ) {}

    public function passes(string $field, $value): bool
    {
        global $db, $empresa;

        $query = $db->where([
            $this->column => $value,
            'codempresa' => $empresa['codempresa']
        ]);

        if ($this->ignoreId) {
            $query->where('id !=', $this->ignoreId);
        }

        return $query->get($this->table)->num_rows() === 0;
    }

    public function message(): string
    {
        return 'Este valor já existe na empresa atual.';
    }
}

// Uso
$rules = [
    'nif' => ['required', new UniqueInCompany('clients', 'nif', $clientId)]
];
```

### Conditional Validation

```php
public function rules(): array
{
    return [
        'type' => 'required|in:person,company',
        'name' => 'required_if:type,person|string|max:255',
        'company_name' => 'required_if:type,company|string|max:255',
        'nif' => 'required_if:type,company|string|size:9',
        'birth_date' => 'required_if:type,person|date|before:18 years ago',

        // Validação condicional complexa
        'invoice_address' => [
            'sometimes',
            function ($field, $value, $error, $allData) {
                if ($allData['ship_to_different_address'] && empty($value)) {
                    return false;
                }
                return true;
            }
        ]
    ];
}
```

**Anti-padrão:**
```php
// ❌ Validação manual inline (Scattered Validation)
public function store(Request $request)
{
    if (empty($request->name)) {
        return ['error' => 'Name is required'];
    }
    // PROBLEMA: Validação não é reutilizável, cada endpoint
    // precisa de repetir as mesmas regras

    if (!filter_var($request->email, FILTER_VALIDATE_EMAIL)) {
        return ['error' => 'Invalid email'];
    }
    // PROBLEMA: Lógica de validação misturada com lógica de negócio,
    // dificulta manutenção e testes

    // ... mais 20 linhas de validação
    // PROBLEMA: Controller fica inchado, violação do Single Responsibility,
    // inconsistência nas mensagens de erro, dificulta internacionalização
}
```

---

## View Engine

### O que é o View Engine?

O **View Engine** é responsável por transformar dados em HTML que o utilizador final vê. É como um "montador de páginas" que pega os seus dados PHP e os transforma em interface visual.

**Conceitos principais:**
- **Templates**: Ficheiros `.blade.php` que misturam HTML com PHP de forma elegante
- **Layouts**: Estrutura base (header, footer, sidebar) reutilizada em várias páginas
- **Components**: Pedaços reutilizáveis de interface (botões, formulários, cards)
- **Data Binding**: Como os dados chegam até à view

**Porquê usar Blade:**
- 🎨 **Sintaxe limpa**: `{{ $user->name }}` em vez de `<?= htmlspecialchars($user->name) ?>`
- 🔄 **Herança**: Layout base + conteúdo específico
- 🛡️ **Segurança**: Escape automático contra XSS
- 📦 **Components**: Reutilização de código
- 🎯 **Directivas**: `@if`, `@foreach` mais legíveis que PHP puro

**Fluxo típico:**
```
Controller → dados → View Template → HTML → Browser
```

### Configuração

```php
// Config/view.php
return [
    'paths' => [
        rootPath('_views'),
        rootPath('Modules/*/Resources/views'),
    ],
    'cache_path' => rootPath("_cache/{$_SERVER['HTTP_HOST']}/views"),
];
```

### Templates Blade

```blade
{{-- resources/views/users/index.blade.php --}}
@extends('layouts.app')

@section('title', 'Users Management')

@section('content')
<div class="container">
    <h1>Users</h1>

    @if(session('success'))
        <div class="alert alert-success">
            {{ session('success') }}
        </div>
    @endif

    <div class="table-responsive">
        <table class="table">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Email</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                @forelse($users as $user)
                    <tr>
                        <td>{{ $user->id }}</td>
                        <td>{{ $user->name }}</td>
                        <td>{{ $user->email }}</td>
                        <td>
                            <a href="{{ route('users.show', $user->id) }}" class="btn btn-sm btn-info">View</a>
                            <a href="{{ route('users.edit', $user->id) }}" class="btn btn-sm btn-warning">Edit</a>
                        </td>
                    </tr>
                @empty
                    <tr>
                        <td colspan="4" class="text-center">No users found</td>
                    </tr>
                @endforelse
            </tbody>
        </table>
    </div>

    {{-- Pagination --}}
    {{ $users->links() }}
</div>
@endsection

@push('scripts')
<script>
    // User-specific JavaScript
    document.addEventListener('DOMContentLoaded', function() {
        console.log('Users page loaded');
    });
</script>
@endpush
```

### Usando Views em Controllers

```php
public function index(): Response
{
    $users = $this->userService->paginate(15);

    return view('users.index', [
        'users' => $users,
        'title' => 'User Management'
    ]);
}

public function create(): Response
{
    return view('users.create', [
        'companies' => $this->companyService->all(),
        'roles' => $this->roleService->all()
    ]);
}
```

### View Composers

```php
// Em um ServiceProvider
public function boot(): void
{
    $viewManager = app(ViewManager::class);

    // Composer para todas as views
    $viewManager->composer('*', function ($view) {
        $view->with('currentUser', auth()->user());
    });

    // Composer específico
    $viewManager->composer('layouts.sidebar', function ($view) {
        $view->with('menuItems', $this->buildMenuItems());
    });
}
```

### Custom Blade Directives

```php
public function boot(): void
{
    $viewManager = app(ViewManager::class);

    // @money($value)
    $viewManager->directive('money', function ($value) {
        return "<?php echo number_format({$value}, 2, ',', '.') . ' €'; ?>";
    });

    // @canModule('users')
    $viewManager->directive('canModule', function ($module) {
        return "<?php if(app('user')->hasModule({$module})): ?>";
    });

    $viewManager->directive('endCanModule', function () {
        return "<?php endif; ?>";
    });
}
```

---

## Legacy Integration

### Porquê Precisamos de Legacy Integration?

O OfficeGest tem **anos de código a funcionar** em produção. Não podemos (nem devemos) reescrever tudo de uma vez. A **Legacy Integration** permite que o código moderno conviva harmoniosamente com o sistema antigo.

**Estratégia de convivência:**
1. **Código novo**: Usa framework moderno (Container, Validation, Queue)
2. **Código antigo**: Continua a funcionar como sempre funcionou
3. **Bridge Classes**: Fazem a ponte entre os dois mundos
4. **Migração gradual**: Aos poucos, partes antigas podem usar recursos modernos

**Variáveis globais disponíveis:**
- `$a` - Aplicação principal (Application class)
- `$u` - Utilizador com sessão iniciada (User class)
- `$db` - Ligação com base de dados
- `$empresa` - Dados da empresa actual
- `$s` - Gestão de sessão
- `$hooks` - Sistema de hooks/eventos
- `$m` - Módulos carregados

### Aceder a Variáveis Globais

```php
<?php
namespace Og\Modules\MyModule\Services;

use BaseClass; // Extends para acessar globals

class UserService extends BaseClass
{
    public function getCurrentCompany(): array
    {
        // Acesso às globals através da BaseClass
        return $this->empresa[$this->buscarCodEmpresa()];
    }

    public function hasPermission(string $module, string $action): bool
    {
        return $this->u->checkPriv("{$module}:{$action}");
    }

    public function logActivity(string $message, array $data = []): void
    {
        // Usando função legacy
        loggerBatch('info', $message, $data);
    }
}
```

### Migração Gradual

```php
// Approach 1: Wrapper para código legacy
class LegacyUserService extends BaseClass
{
    public function create(array $data): array
    {
        // Usar validation moderna
        $validator = app(Validator::class);
        $validator->setRules([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users'
        ])->withRequest($data);

        if (!$validator->validate()) {
            throw new ValidationException($validator->errors());
        }

        // Mas ainda usar o database legacy
        $userId = $this->db->insert('users', $validator->validated());

        // E dispatch modern queue job
        app(QueueManager::class)->push(new SendWelcomeEmailJob($data['email']));

        return $this->db->where('id', $userId)->get('users')->row();
    }
}
```

---

## Exemplos Práticos

### Exemplo 1: CRUD Completo de Products

```php
<?php
// Modules/Inventory/Controllers/ProductController.php
namespace Og\Modules\Inventory\Controllers;

use Og\Modules\Common\Http\Request;
use Og\Modules\Common\Http\Response;
use Og\Modules\Inventory\Services\ProductService;
use Og\Modules\Inventory\Requests\StoreProductRequest;
use Og\Modules\Inventory\Jobs\UpdateStockLevelsJob;

class ProductController
{
    public function __construct(
        private ProductService $productService,
        private Response $response
    ) {}

    public function index(Request $request): Response
    {
        $filters = $request->only(['category', 'status', 'search']);
        $products = $this->productService->paginate($filters, 20);

        return \Funcs::view('inventory.products.index', compact('products', 'filters'));
    }

    public function store(StoreProductRequest $request): Response
    {
        $product = $this->productService->create($request->validated());

        // Trigger stock level recalculation
        app(QueueManager::class)->push(new UpdateStockLevelsJob($product->id));

        return $this->response
            ->json($product, 201)
            ->header('Location', "/api/products/{$product->id}");
    }

    public function bulkUpdate(Request $request): Response
    {
        $productIds = $request->get('product_ids', []);
        $updates = $request->get('updates', []);

        // Validate bulk operation
        $validator = app(Validator::class);
        $validator->setRules([
            'product_ids' => 'required|array|min:1',
            'product_ids.*' => 'integer|exists:products,id',
            'updates' => 'required|array',
            'updates.price' => 'sometimes|numeric|min:0',
            'updates.status' => 'sometimes|in:active,inactive,discontinued'
        ])->withRequest($request->all());

        if (!$validator->validate()) {
            return $this->response->json(['errors' => $validator->errors()], 422);
        }

        // Process in background for large operations
        if (count($productIds) > 50) {
            $jobId = app(QueueManager::class)->push(
                new BulkUpdateProductsJob($productIds, $updates)
            );

            return $this->response->json([
                'message' => 'Bulk update queued',
                'job_id' => $jobId
            ], 202);
        }

        // Process immediately for small operations
        $updated = $this->productService->bulkUpdate($productIds, $updates);

        return $this->response->json([
            'message' => "Updated {$updated} products",
            'updated_count' => $updated
        ]);
    }
}

// Service Layer
<?php
namespace Og\Modules\Inventory\Services;

class ProductService extends BaseClass
{
    public function create(array $data): array
    {
        return $this->db->transaction(function() use ($data) {
            $productId = $this->db->insert('products', [
                'name' => $data['name'],
                'sku' => $data['sku'],
                'price' => $data['price'],
                'codempresa' => $this->buscarCodEmpresa(),
                'created_at' => date('Y-m-d H:i:s')
            ]);

            // Create initial stock record
            if (isset($data['initial_stock'])) {
                $this->db->insert('stock_movements', [
                    'product_id' => $productId,
                    'quantity' => $data['initial_stock'],
                    'type' => 'initial',
                    'created_at' => date('Y-m-d H:i:s')
                ]);
            }

            return $this->find($productId);
        });
    }

    public function paginate(array $filters = [], int $perPage = 15): array
    {
        $query = $this->db->from('products p')
                          ->where('p.codempresa', $this->buscarCodEmpresa());

        if (!empty($filters['category'])) {
            $query->where('p.category_id', $filters['category']);
        }

        if (!empty($filters['status'])) {
            $query->where('p.status', $filters['status']);
        }

        if (!empty($filters['search'])) {
            $query->group_start()
                  ->like('p.name', $filters['search'])
                  ->or_like('p.sku', $filters['search'])
                  ->group_end();
        }

        $total = $query->count_all_results('', false);
        $products = $query->limit($perPage)
                         ->offset(($filters['page'] ?? 1 - 1) * $perPage)
                         ->get()
                         ->result();

        return [
            'data' => $products,
            'total' => $total,
            'per_page' => $perPage,
            'current_page' => $filters['page'] ?? 1
        ];
    }
}
```

### Exemplo 2: Sistema de Notificações

```php
<?php
// Modules/Common/Notifications/NotificationService.php
namespace Og\Modules\Common\Notifications;

use Og\Modules\Common\Queue\QueueManager;

class NotificationService extends BaseClass
{
    public function __construct(
        private QueueManager $queue
    ) {
        parent::__construct();
    }

    public function notify(string $type, array $recipients, array $data): void
    {
        foreach ($recipients as $recipient) {
            $this->queue->push(new SendNotificationJob([
                'type' => $type,
                'recipient' => $recipient,
                'data' => $data,
                'company_id' => $this->buscarCodEmpresa()
            ]));
        }
    }

    public function notifyCompanyUsers(string $type, array $data, array $userRoles = []): void
    {
        $users = $this->getCompanyUsers($userRoles);
        $this->notify($type, $users, $data);
    }

    private function getCompanyUsers(array $roles = []): array
    {
        $query = $this->db->select('email, nome')
                          ->from('empregados')
                          ->where('codempresa', $this->buscarCodEmpresa())
                          ->where('ativo', 'S');

        if (!empty($roles)) {
            $query->where_in('role', $roles);
        }

        return $query->get()->result();
    }
}

// Job para envio
<?php
namespace Og\Modules\Common\Notifications\Jobs;

class SendNotificationJob implements JobInterface
{
    public function __construct(private array $data) {}

    public function handle(): void
    {
        $notifier = match($this->data['type']) {
            'email' => new EmailNotifier(),
            'sms' => new SmsNotifier(),
            'push' => new PushNotifier(),
            default => throw new \InvalidArgumentException("Unknown notification type")
        };

        $notifier->send($this->data['recipient'], $this->data['data']);
    }
}

// Usage no controller
public function approveInvoice(int $invoiceId): Response
{
    $invoice = $this->invoiceService->approve($invoiceId);

    // Notify accounting team
    app(NotificationService::class)->notifyCompanyUsers('email', [
        'title' => 'Invoice Approved',
        'message' => "Invoice #{$invoice->number} has been approved",
        'url' => "/invoices/{$invoiceId}"
    ], ['accounting', 'manager']);

    return $this->response->json(['message' => 'Invoice approved']);
}
```

### Exemplo 3: API Rate Limiting

```php
<?php
// Modules/Common/Http/Middleware/ApiRateLimitMiddleware.php
namespace Og\Modules\Common\Http\Middleware;

use Og\Modules\Common\Http\Request;
use Og\Modules\Common\Http\Response;

class ApiRateLimitMiddleware extends BaseClass
{
    public function handle(Request $request, callable $next): Response
    {
        $key = $this->getRateLimitKey($request);
        $limit = $this->getRateLimit($request);

        $attempts = (int) cache()->get($key, 0);

        if ($attempts >= $limit['max']) {
            return app(Response::class)->json([
                'error' => 'Rate limit exceeded',
                'retry_after' => $limit['window']
            ], 429)->header('Retry-After', $limit['window']);
        }

        // Increment counter
        cache()->put($key, $attempts + 1, $limit['window']);

        $response = $next($request);

        // Add rate limit headers
        return $response
            ->header('X-RateLimit-Limit', $limit['max'])
            ->header('X-RateLimit-Remaining', max(0, $limit['max'] - $attempts - 1))
            ->header('X-RateLimit-Reset', time() + $limit['window']);
    }

    private function getRateLimitKey(Request $request): string
    {
        // Use company + user for authenticated requests
        if ($token = $request->header('Authorization')) {
            $user = $this->getUserFromToken($token);
            return "rate_limit:company_{$user['codempresa']}:user_{$user['id']}";
        }

        // Use IP for unauthenticated requests
        return "rate_limit:ip_{$request->ip()}";
    }

    private function getRateLimit(Request $request): array
    {
        // Different limits based on user type or API endpoint
        if ($request->is('api/public/*')) {
            return ['max' => 100, 'window' => 3600]; // 100/hour for public API
        }

        if ($request->is('api/admin/*')) {
            return ['max' => 1000, 'window' => 3600]; // 1000/hour for admin API
        }

        return ['max' => 300, 'window' => 3600]; // 300/hour default
    }
}
```

---

## Anti-padrões e Boas Práticas

### ❌ Anti-padrões Comuns

#### 1. God Controller
```php
// ❌ NÃO faça isso (God Controller Anti-pattern)
class UserController
{
    public function store(Request $request)
    {
        // 200 linhas de validação, business logic, database operations...
        // PROBLEMA: Violação do Single Responsibility Principle,
        // código impossível de testar unitariamente,
        // dificulta manutenção e reutilização,
        // controller vira um "deus" que faz tudo
    }
}

// ✅ Faça assim (Single Responsibility)
class UserController
{
    public function store(StoreUserRequest $request)
    {
        $user = $this->userService->create($request->validated());
        return $this->response->json($user, 201);
        // BENEFÍCIO: Controller só coordena, cada classe tem uma responsabilidade,
        // fácil de testar, manter e reutilizar
    }
}
```

#### 2. Direct Database Access em Controllers
```php
// ❌ NÃO faça isso
public function getUsers()
{
    global $db;
    return $db->get('users')->result();
}

// ✅ Use Services
public function getUsers()
{
    return $this->userService->all();
}
```

#### 3. Hard-coded Values
```php
// ❌ NÃO faça isso
if ($user->role === 'admin') {
    // código...
}

// ✅ Use constantes/config
if ($user->role === UserRole::ADMIN) {
    // ou
}
if ($user->hasRole(config('roles.admin'))) {
    // código...
}
```

#### 4. Sem tratamento de erros
```php
// ❌ NÃO faça isso
public function deleteUser(int $id)
{
    $this->db->delete('users', ['id' => $id]);
    return 'deleted';
}

// ✅ Com tratamento apropriado
public function deleteUser(int $id): Response
{
    try {
        if (!$this->userService->exists($id)) {
            return $this->response->json(['error' => 'User not found'], 404);
        }

        $this->userService->delete($id);
        return $this->response->noContent();

    } catch (\Exception $e) {
        loggerBatch('error', 'Failed to delete user', [
            'user_id' => $id,
            'error' => $e->getMessage()
        ]);

        return $this->response->json(['error' => 'Internal server error'], 500);
    }
}
```

### ✅ Boas Práticas

#### 1. Single Responsibility
```php
// Controller apenas coordena
class InvoiceController
{
    public function approve(int $id, ApproveInvoiceRequest $request)
    {
        $invoice = $this->invoiceService->approve($id, $request->validated());
        $this->notificationService->notifyApproval($invoice);
        return $this->response->json($invoice);
    }
}

// Service faz a lógica de negócio
class InvoiceService
{
    public function approve(int $id, array $data): Invoice
    {
        // Lógica de aprovação aqui
    }
}
```

#### 2. Proper Error Handling
```php
public function process(): Response
{
    try {
        $result = $this->complexOperation();
        return $this->response->json($result);

    } catch (ValidationException $e) {
        return $this->response->json(['errors' => $e->errors()], 422);
    } catch (AuthorizationException $e) {
        return $this->response->json(['error' => 'Unauthorized'], 403);
    } catch (\Exception $e) {
        loggerBatch('error', 'Unexpected error', [
            'error' => $e->getMessage(),
            'trace' => $e->getTraceAsString()
        ]);
        return $this->response->json(['error' => 'Internal error'], 500);
    }
}
```

#### 3. Use Type Hints
```php
public function calculateDiscount(
    float $amount,
    ?string $couponCode = null,
    User $user
): DiscountResult {
    // Implementação
}
```

#### 4. Resource Management
```php
// Para operações que podem consumir memoria
public function processBigFile(string $filePath): void
{
    $handle = fopen($filePath, 'r');

    try {
        while (($line = fgets($handle)) !== false) {
            $this->processLine($line);
        }
    } finally {
        fclose($handle);
    }
}
```

---

## Convenções

### Naming

#### Classes
- **Controllers**: `UserController`, `InvoiceController`
- **Services**: `UserService`, `EmailService`
- **Jobs**: `SendEmailJob`, `ProcessInvoiceJob`
- **Requests**: `StoreUserRequest`, `UpdateInvoiceRequest`
- **Middleware**: `AuthMiddleware`, `RateLimitMiddleware`

#### Methods
- **CRUD**: `index()`, `show()`, `store()`, `update()`, `destroy()`
- **Actions**: `approve()`, `cancel()`, `process()`
- **Boolean**: `isActive()`, `hasPermission()`, `canEdit()`

#### Variables
```php
// Camel case para variables
$userName = 'John Doe';
$isActive = true;

// Snake case para array keys (database fields)
$user = [
    'user_name' => 'John Doe',
    'is_active' => true
];
```

### File Organization

```
Modules/MyModule/
├── Controllers/
├── Services/
├── Requests/
├── Jobs/
├── Middleware/
├── Rules/
├── Routes/
│   ├── web.php
│   └── api.php
├── Resources/
│   └── views/
└── Providers/
    └── MyModuleServiceProvider.php
```

### Documentation

```php
/**
 * Process user registration with email verification
 *
 * @param array $userData User data from registration form
 * @param bool $sendEmail Whether to send verification email
 * @return User Created user instance
 *
 * @throws ValidationException When user data is invalid
 * @throws EmailException When email sending fails
 */
public function register(array $userData, bool $sendEmail = true): User
{
    // Implementation
}
```

### Testing Patterns

```php
// Embora não possamos criar testes automatizados,
// estruture o código pensando em testabilidade:

// ❌ Difícil de testar
class UserService
{
    public function createUser(array $data)
    {
        $user = new User($data);
        $user->save(); // Direct database call
        mail($user->email, 'Welcome', '...'); // Direct mail call
        return $user;
    }
}

// ✅ Fácil de testar
class UserService
{
    public function __construct(
        private UserRepository $userRepo,
        private EmailService $emailService
    ) {}

    public function createUser(array $data): User
    {
        $user = $this->userRepo->create($data);
        $this->emailService->sendWelcomeEmail($user);
        return $user;
    }
}
```

---

## HTTP Client (Laravel Bridge)

O framework usa o HTTP Client do Laravel através de uma facade. Para uso completo, consulte a [documentação oficial do Laravel 12.x](https://laravel.com/docs/12.x/http-client).

### Exemplo Básico
```php
use Og\Modules\Common\HttpClient\HttpClientFactory;

$client = app(HttpClientFactory::class);

// GET request
$response = $client->get('https://api.example.com/users');
$users = $response->json();

// POST with data
$response = $client->post('https://api.example.com/users', [
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

// With headers and timeout
$response = $client->withHeaders([
    'Authorization' => 'Bearer ' . $token,
    'Accept' => 'application/json'
])->timeout(30)->get('https://api.example.com/protected');
```

---

## Próximos Passos

Para começar a usar o framework:

1. **Estude os exemplos** nesta documentação
2. **Crie um módulo simples** usando os generators: `php og make:module TestModule`
3. **Implemente um CRUD** seguindo os padrões mostrados
4. **Use o Queue System** para operações demoradas
5. **Aplique validação** em todos os inputs
6. **Integre gradualmente** com código legacy existente

**Lembre-se**: O framework é evolutivo. Use-o onde faz sentido, mas não force a sua aplicação onde o código legado funciona bem.

---

## Suporte

Para dúvidas ou problemas:

- Consulte esta documentação primeiro
- Verifique os exemplos práticos
- Analise o código fonte em `Modules/Common/`
- Procure padrões similares no código existente

**Mantra**: Código simples, claro e que funciona é melhor que código complexo e "elegante".
