# Sistema de Notificações - Framework OfficeGest

## Visão Geral

O **Sistema de Notificações** do OfficeGest é uma implementação robusta e extensível para envio de notificações através de múltiplos canais. Construído sobre os padrões **Strategy**, **Observer** e **Service Provider**, oferece uma API fluente para notificações síncronas através de base de dados, email e Mattermost.

```mermaid
flowchart LR
    subgraph Input["Origem"]
        Code["Código da Aplicação"]
        CLI["CLI/Worker"]
        Helper["Helper notify()"]
    end
    
    subgraph Core["NotificationService"]
        Service["Fluent API\n• using()\n• debugging()\n• send()"]
    end
    
    subgraph Channels["Canais"]
        DB[(DatabaseChannel)]
        Email[EmailChannel]
        MM[MattermostChannel]
    end
    
    Code --> Service
    CLI --> Service
    Helper --> Service
    Service --> DB
    Service --> Email
    Service --> MM
    
    style Core fill:#e8f4fd,stroke:#0284c7
    style Channels fill:#f0fdf4,stroke:#22c55e
```

---

## Configuração

O sistema é configurado através do ficheiro `Config/notifications.php`:

```php
<?php
// Modules/Common/Config/notifications.php

return [
    // Canal padrão para notificações
    'default' => envVar('NOTIFICATION_DEFAULT_CHANNEL', 'database'),
    
    // Modo de debugging (apenas envia se true)
    'debugging' => envVar('NOTIFICATION_DEBUG', false),
    
    'channels' => [
        'database' => [
            'class' => \Og\Modules\Common\Notifications\Channels\DatabaseChannel::class,
            'enabled' => true,
        ],
        'mattermost' => [
            'enabled' => envVar('MATTERMOST_NOTIFICATIONS_ENABLED', false),
            'api_url' => envVar('MATTERMOST_NOTIFICATIONS_URL', ''),
            'token' => envVar('MATTERMOST_TOKEN', ''),
            'channel_id' => envVar('MATTERMOST_NOTIFICATIONS_CHANNEL_ID', ''),
            'icon_url' => envVar('MATTERMOST_ICON_URL', ''),
        ],
        'email' => [
            'class' => \Og\Modules\Common\Notifications\Channels\EmailChannel::class,
            'enabled' => true,
        ],
    ]
];
```

### Variáveis de Ambiente

| Variável | Descrição | Default |
|----------|-----------|---------|
| `NOTIFICATION_DEFAULT_CHANNEL` | Canal padrão | `database` |
| `NOTIFICATION_DEBUG` | Habilita modo debug | `false` |
| `MATTERMOST_NOTIFICATIONS_ENABLED` | Activa Mattermost | `false` |
| `MATTERMOST_NOTIFICATIONS_URL` | URL da API Mattermost | - |
| `MATTERMOST_TOKEN` | Token de autenticação | - |
| `MATTERMOST_NOTIFICATIONS_CHANNEL_ID` | ID do canal destino | - |

---

## Arquitectura de Componentes

```mermaid
classDiagram
    class NotificationChannelInterface {
        <<interface>>
        +send(array notification) bool
        +shouldSend(array notification) bool
    }
    
    class FormatterInterface {
        <<interface>>
        +format(data) string
    }
    
    class NotificationService {
        -array channels
        -array formatters
        -array selectedChannels
        -bool isDebugging
        +addChannel(channel) self
        +addFormatter(name, formatter) self
        +using(channelNames) self
        +debugging() self
        +send(notification) array
        +formatData(data) array
    }
    
    class DatabaseChannel {
        -CentroMensagens centroMensagens
        +send(notification) bool
        +shouldSend(notification) bool
    }
    
    class EmailChannel {
        -Email emailService
        +send(notification) bool
        +shouldSend(notification) bool
    }
    
    class MattermostChannel {
        -Client httpClient
        -string apiUrl
        -string token
        -string channelId
        +send(notification) bool
        +shouldSend(notification) bool
    }
    
    NotificationChannelInterface <|.. DatabaseChannel
    NotificationChannelInterface <|.. EmailChannel
    NotificationChannelInterface <|.. MattermostChannel
    NotificationService o-- NotificationChannelInterface
    NotificationService o-- FormatterInterface
```

### Estrutura de Ficheiros

```
Modules/Common/Notifications/
├── Services/
│   └── NotificationService.php      # Serviço central
├── Channels/
│   ├── DatabaseChannel.php          # Canal base de dados
│   ├── EmailChannel.php             # Canal email
│   └── MattermostChannel.php        # Canal Mattermost
├── Contracts/
│   ├── NotificationChannelInterface.php
│   └── FormatterInterface.php
├── Formatters/
│   ├── ContextFormatter.php         # Contexto geral
│   ├── JobContextFormatter.php      # Contexto de jobs
│   └── StackTraceFormatter.php      # Stack traces
├── EnvironmentNotifiers/
│   └── CliNotifier.php              # Notificador CLI
└── Providers/
    └── NotificationServiceProvider.php
```

---

## Facade Notification

A facade `Notification` fornece acesso simplificado ao `NotificationService`:

```php
<?php

namespace Og\Modules\Common\Facades;

use Og\Modules\Common\Notifications\Services\NotificationService;
use Og\Modules\Common\Utils\Facade;

/**
 * @method static NotificationService send(array $array)
 * @method static NotificationService using(string|array $class)
 * @method static NotificationService debugging()
 */
class Notification extends Facade
{
    public static function getFacadeAccessor(): string
    {
        return NotificationService::class;
    }
}
```

### Uso Básico

```php
use Og\Modules\Common\Facades\Notification;

// Envio simples (usa canal padrão: DatabaseChannel)
Notification::send([
    'text' => 'Operação concluída com sucesso!',
    'subject' => 'Notificação do Sistema',
    'color' => 'success'
]);
```

### Especificar Canal

```php
use Og\Modules\Common\Facades\Notification;
use Og\Modules\Common\Notifications\Channels\MattermostChannel;
use Og\Modules\Common\Notifications\Channels\EmailChannel;

// Enviar apenas para Mattermost
Notification::using(MattermostChannel::class)->send([
    'text' => 'Alerta crítico do sistema!',
    'subject' => 'Erro Crítico',
    'color' => 'danger'
]);

// Enviar para múltiplos canais
Notification::using([
    MattermostChannel::class,
    EmailChannel::class
])->send([
    'text' => 'Sistema actualizado',
    'to' => 'admin@empresa.com',
    'subject' => 'Actualização',
    'body' => '<p>O sistema foi actualizado com sucesso.</p>'
]);
```

### Modo Debugging

```php
// Só envia se NOTIFICATION_DEBUG=true
Notification::using(MattermostChannel::class)
    ->debugging()
    ->send([
        'text' => 'Debug: Variáveis de sessão',
        'color' => 'info'
    ]);
```

---

## Helper Global: notify()

A função `notify()` oferece uma interface simplificada para notificações rápidas:

```php
function notify(
    string $message,              // Corpo da mensagem
    ?string $subject = null,      // Assunto
    ?int $userId = null,          // ID do destinatário (null = utilizador actual)
    null|string|Carbon $date = null,  // Data do alerta
    ?string $url = null,          // URL associado
    string $type = 'success',     // Tipo: success, warning, error, info
    array $options = []           // Opções adicionais
): void
```

### Exemplos de Uso

```php
// Notificação simples para o utilizador actual
notify('Documento guardado com sucesso!');

// Com destinatário específico
notify(
    message: 'Nova tarefa atribuída',
    subject: 'Gestão de Tarefas',
    userId: 42
);

// Com URL de acção
notify(
    message: 'Novo pedido recebido #12345',
    subject: 'Pedidos',
    url: '/pedidos/ver/12345',
    type: 'info'
);

// Agendamento futuro
notify(
    message: 'Lembrete: Reunião em 1 hora',
    date: now()->addHour(),
    type: 'warning'
);

// Especificar canais (formato novo)
notify(
    message: 'Sistema em manutenção',
    type: 'warning',
    options: [
        'channels' => [MattermostChannel::class, DatabaseChannel::class]
    ]
);

// Compatibilidade legacy
notify(
    message: 'Alerta crítico',
    type: 'error',
    options: [
        'mattermost' => true,
        'database' => false
    ]
);

// Com detalhes adicionais
notify(
    message: 'Importação concluída',
    subject: 'Importação de Dados',
    type: 'success',
    options: [
        'details' => [
            'records_imported' => 150,
            'duration' => '2.5s'
        ]
    ]
);
```

---

## Canais de Notificação

### DatabaseChannel

Integra com o sistema legacy `CentroMensagens` para notificações internas:

```php
<?php

namespace Og\Modules\Common\Notifications\Channels;

use CentroMensagens;
use Og\Modules\Common\Notifications\Contracts\NotificationChannelInterface;

class DatabaseChannel implements NotificationChannelInterface
{
    private CentroMensagens $centroMensagens;

    public function __construct(CentroMensagens $centroMensagens)
    {
        $this->centroMensagens = $centroMensagens;
    }

    public function send(array $notification): bool
    {
        $result = $this->centroMensagens->create_notifications($notification);
        return $result['success'] ?? false;
    }

    public function shouldSend(array $notification): bool
    {
        return $notification['database'] ?? true;
    }
}
```

**Campos Suportados:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| `text` | string | Corpo da mensagem |
| `subject` | string | Assunto/título |
| `codempr` | int | ID do utilizador destinatário |
| `date_alert` | string | Data/hora do alerta (Y-m-d H:i:s) |
| `url` | string | URL de acção |
| `color` | string | Tipo visual (success, warning, error, info) |

---

### EmailChannel

Canal readonly para envio de emails com suporte a múltiplos destinatários:

```php
<?php

namespace Og\Modules\Common\Notifications\Channels;

use Og\Modules\Common\Notifications\Contracts\NotificationChannelInterface;

readonly class EmailChannel implements NotificationChannelInterface
{
    public function __construct(private \Email $emailService)
    {
    }

    public function send(array $notification): bool
    {
        // Validação de campos obrigatórios
        if (empty($notification['to']) || 
            empty($notification['subject']) || 
            empty($notification['body'])) {
            return false;
        }
        
        $to = $notification['to'];
        $subject = $notification['subject'];
        $body = $notification['body'];
        $from = $notification['from'] ?? [];
        $args = $notification['args'] ?? ['return_data' => 1];
        
        // Suporte a array de destinatários
        if (is_array($to)) {
            foreach ($to as $item) {
                $this->emailService->SendNow(
                    from: $from, 
                    to: $item, 
                    subject: $subject, 
                    content: $body, 
                    args: $args
                );
            }
            return true;
        }
        
        // Suporte a string separada por vírgulas
        if (is_string($to) && str_contains($to, ',')) {
            $destinies = explode(',', $to);
            foreach ($destinies as $destiny) {
                $this->emailService->SendNow(
                    from: $from, 
                    to: trim($destiny), 
                    subject: $subject, 
                    content: $body, 
                    args: $args
                );
            }
            return true;
        }
        
        $result = $this->emailService->SendNow(
            from: $from, 
            to: $to, 
            subject: $subject, 
            content: $body, 
            args: $args
        );
        
        return $result['success'] ?? false;
    }

    public function shouldSend(array $notification): bool
    {
        return $notification['email'] ?? true;
    }
}
```

**Campos Obrigatórios:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| `to` | string\|array | Destinatário(s) |
| `subject` | string | Assunto do email |
| `body` | string | Conteúdo HTML |

**Campos Opcionais:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| `from` | array | Remetente personalizado |
| `args` | array | Argumentos adicionais do serviço Email |

---

### MattermostChannel

Integração completa com Mattermost API v4, incluindo rich formatting:

```php
<?php

namespace Og\Modules\Common\Notifications\Channels;

use GuzzleHttp\Client;
use Og\Modules\Common\Notifications\Contracts\NotificationChannelInterface;

class MattermostChannel implements NotificationChannelInterface
{
    private Client $httpClient;
    private string $apiUrl;
    private string $token;
    private string $channelId;

    public function __construct(string $apiUrl, string $token, string $channelId)
    {
        $this->apiUrl = $apiUrl;
        $this->token = $token;
        $this->channelId = $channelId;
        $this->httpClient = new Client();
    }

    public function send(array $notification): bool
    {
        try {
            $mentions = $notification['mattermost_mentions'] ?? ['@channel'];
            $mentionsText = implode(' ', $mentions);
            $message = $mentionsText . ' ' . $notification['text'];

            $response = $this->httpClient->post("{$this->apiUrl}/api/v4/posts", [
                'headers' => [
                    'Authorization' => "Bearer {$this->token}",
                    'Content-Type' => 'application/json'
                ],
                'json' => [
                    'channel_id' => $this->channelId,
                    'message' => $message,
                    'props' => [
                        'attachments' => [
                            [
                                'title' => $notification['subject'] ?? '',
                                'text' => $notification['formatted_text'] ?? '',
                                'color' => $this->getColor($notification['color'] ?? 'success'),
                                'fields' => $notification['fields'] ?? [],
                                'footer' => 'OfficeGest Notification',
                            ]
                        ],
                        'markdown' => true,
                        'from_bot' => true,
                    ]
                ]
            ]);
            
            return $response->getStatusCode() >= 200 && $response->getStatusCode() < 300;
        } catch (\Throwable $e) {
            loggerBatch('error', [
                'message' => 'Error sending notification to Mattermost',
                'exception' => $e->getMessage(),
            ]);
            return false;
        }
    }

    private function getColor(string $type): string
    {
        return match ($type) {
            'error', 'danger' => '#e01e5a',
            'warning' => '#ecb22e',
            'info' => '#2eb6f0',
            default => '#36a64f'  // success
        };
    }

    public function shouldSend(array $notification): bool
    {
        return true;
    }
}
```

**Campos Suportados:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| `text` | string | Mensagem principal |
| `subject` | string | Título do attachment |
| `formatted_text` | string | Texto formatado (markdown) |
| `color` | string | Cor: success, warning, error, info, danger |
| `fields` | array | Campos estruturados do Mattermost |
| `mattermost_mentions` | array | Menções (default: `['@channel']`) |

**Exemplo Avançado:**

```php
Notification::using(MattermostChannel::class)->send([
    'text' => 'Deploy concluído',
    'subject' => 'Produção Actualizada',
    'formatted_text' => "### Detalhes\n- Branch: main\n- Commit: abc123",
    'color' => 'success',
    'mattermost_mentions' => ['@devops', '@backend'],
    'fields' => [
        ['title' => 'Versão', 'value' => '2.1.0', 'short' => true],
        ['title' => 'Ambiente', 'value' => 'production', 'short' => true],
    ]
]);
```

---

## Sistema de Formatadores

Os formatadores transformam dados em strings formatadas para apresentação nas notificações.

### ContextFormatter

Formata informações de contexto geral:

```php
<?php

namespace Og\Modules\Common\Notifications\Formatters;

use Og\Modules\Common\Notifications\Contracts\FormatterInterface;

class ContextFormatter implements FormatterInterface
{
    public function format($data): string
    {
        if (!is_array($data)) {
            return '';
        }

        $result = "### Context\n";

        $basicFields = ['request_id', 'environment', 'timestamp', 'application'];

        foreach ($basicFields as $field) {
            if (isset($data[$field])) {
                $value = is_array($data[$field]) ? json_encode($data[$field]) : $data[$field];
                $result .= "- **{$field}**: {$value}\n";
            }
        }

        if (isset($data['additional']) && is_array($data['additional'])) {
            foreach ($data['additional'] as $key => $value) {
                $formattedValue = is_array($value) ? json_encode($value) : $value;
                $result .= "- **{$key}**: {$formattedValue}\n";
            }
        }

        return $result;
    }
}
```

**Uso:**

```php
$service = app(NotificationService::class);

$formatted = $service->formatData([
    'context' => [
        'request_id' => 'req-abc123',
        'environment' => 'production',
        'timestamp' => now()->toIso8601String(),
        'additional' => [
            'user_agent' => 'Mozilla/5.0...',
            'ip_address' => '192.168.1.1'
        ]
    ]
]);

// Output:
// ### Context
// - **request_id**: req-abc123
// - **environment**: production
// - **timestamp**: 2024-01-15T10:30:00+00:00
// - **user_agent**: Mozilla/5.0...
// - **ip_address**: 192.168.1.1
```

---

### JobContextFormatter

Formata informações específicas de jobs de queue:

```php
<?php

namespace Og\Modules\Common\Notifications\Formatters;

use Og\Modules\Common\Notifications\Contracts\FormatterInterface;

class JobContextFormatter implements FormatterInterface
{
    public function format($data): string
    {
        if (!is_array($data)) {
            return '';
        }

        $result = "### Informações do Job\n";

        $jobFields = [
            'job_id'       => 'ID',
            'queue'        => 'Queue',
            'max_attempts' => 'Max Tries',
            'status'       => 'Status',
            'payload'      => 'Payload',
            'started_at'   => 'Started At',
            'finished_at'  => 'Finished At',
            'company_id'   => 'Company ID',
            'owner_id'     => 'Owner ID',
            'domain'       => 'Domain',
            'job_class'    => 'Job Class',
            'worker_id'    => 'Worker ID',
            'attempts'     => 'Attempts',
        ];

        foreach ($jobFields as $field => $label) {
            if (isset($data[$field])) {
                $value = is_array($data[$field])
                    ? json_encode($data[$field])
                    : $data[$field];

                $result .= "- **{$label}**: {$value}\n";
            }
        }

        return $result;
    }
}
```

---

### StackTraceFormatter

Formata excepções e stack traces para debugging:

```php
<?php

namespace Og\Modules\Common\Notifications\Formatters;

use Og\Modules\Common\Notifications\Contracts\FormatterInterface;

class StackTraceFormatter implements FormatterInterface
{
    public function format($data): string
    {
        if (!$data instanceof \Throwable) {
            return '';
        }

        $result = "### Error Details\n";
        $result .= "- **Type**: " . get_class($data) . "\n";
        $result .= "- **Message**: " . $data->getMessage() . "\n";
        $result .= "- **File**: " . $data->getFile() . ":" . $data->getLine() . "\n\n";

        $result .= "### Stack Trace\n";
        $result .= "```\n" . $data->getTraceAsString() . "\n```";

        return $result;
    }
}
```

**Uso Combinado:**

```php
try {
    // código que pode falhar
} catch (\Throwable $e) {
    $service = app(NotificationService::class);
    
    $formatted = $service->formatData([
        'job_context' => [
            'job_id' => 'job-123',
            'queue' => 'default',
            'attempts' => 2,
            'job_class' => 'SendInvoiceJob'
        ],
        'exception' => $e
    ]);

    Notification::using(MattermostChannel::class)->send([
        'text' => 'Job execution failed',
        'subject' => 'Queue System Alert',
        'color' => 'danger',
        'formatted_text' => $formatted['job_context'] . "\n" . $formatted['exception']
    ]);
}
```

---

## CliNotifier - Notificações CLI

O `CliNotifier` é um componente especializado para notificações em ambiente CLI (workers, commands, cron jobs). Oferece mais de 40 métodos específicos para diferentes cenários de debugging.

### Níveis de Notificação

```mermaid
flowchart TD
    subgraph Debug["Debug (Condicional)"]
        D["debug()"]
        W["warning()"]
    end
    
    subgraph Critical["Crítico (Sempre Enviado)"]
        C["critical()"]
    end
    
    Debug -->|"NOTIFICATION_DEBUG=true"| MM[(Mattermost)]
    Critical -->|"Sempre"| MM
    
    style Critical fill:#fee2e2,stroke:#ef4444
    style Debug fill:#fef3c7,stroke:#f59e0b
```

### Métodos Base

```php
use Og\Modules\Common\Notifications\EnvironmentNotifiers\CliNotifier;

// Debug - só envia se NOTIFICATION_DEBUG=true e ambiente CLI
CliNotifier::debug('Processamento iniciado', [
    'batch_size' => 100,
    'start_time' => date('H:i:s')
]);

// Warning - debug com cor warning
CliNotifier::warning('Memória elevada', [
    'usage' => '450MB'
]);

// Critical - SEMPRE enviado em ambiente CLI
CliNotifier::critical('Falha crítica no sistema', [
    'error' => 'Database connection lost',
    'last_query' => 'SELECT * FROM users'
]);
```

### Notificações de Bootstrap

```php
// Addon loading
CliNotifier::addonLoadingStarted();
CliNotifier::addonLoading('saft_ao', '/path/to/addon.php');
CliNotifier::addonLoaded('saft_ao');
CliNotifier::addonClassNotFound('saft_ao', 'SaftAoClass');
CliNotifier::addonFileNotFound('saft_ao', '/missing/path.php');
CliNotifier::addonLoadingFailed('saft_ao', $exception);
CliNotifier::addonLoadingCompleted(['saft_ao', 'crm', 'billing']);

// Database & Redis
CliNotifier::databaseConnected(45.2, 'MySQL');
CliNotifier::databaseFailed('Connection refused');
CliNotifier::redisConnected('cluster', ['192.168.1.1:6379', '192.168.1.2:6379']);
CliNotifier::redisFailed(['192.168.1.1:6379'], 'Timeout');
```

### Notificações de Queue/Worker

```php
// Worker lifecycle
CliNotifier::workerStarting([
    'vhost' => 'empresa123',
    'queue' => 'default',
    'memory' => 256,
    'name' => 'worker-1'
]);

// Job lifecycle
CliNotifier::queueJobPopped('default', 'found');
CliNotifier::queueJobDeserializing($jobId, SendEmailJob::class);
CliNotifier::queueJobDeserialized($jobId, SendEmailJob::class);
CliNotifier::queueJobProcessingStarted($jobId, SendEmailJob::class, 1, 3);
CliNotifier::queueJobEnteringSandbox($jobId, SendEmailJob::class);
CliNotifier::queueJobHandleStarted($jobId, SendEmailJob::class);
CliNotifier::queueJobHandleCompleted($jobId, SendEmailJob::class);

// Job failures
CliNotifier::queueJobFailed($jobId, SendEmailJob::class, $exception, 2, 3);
CliNotifier::queueJobScheduledRetry($jobId, SendEmailJob::class, 2, 60);
CliNotifier::queueJobMovedToDeadLetter($jobId, SendEmailJob::class, 'Max retries exceeded');

// Worker issues
CliNotifier::queueWorkerException($exception, 'worker-1');
CliNotifier::memoryWarning(450.5, 512.0, 'worker-1');
```

### Notificações NAPS (Jobs com Lock)

```php
// NAPS job lifecycle
CliNotifier::napsJobStarted(GenerateSaftJob::class, 'saft:empresa:2024');
CliNotifier::napsJobCompleted(GenerateSaftJob::class, 'saft:empresa:2024');
CliNotifier::napsJobFailed(GenerateSaftJob::class, 'saft:empresa:2024', 'Timeout');
CliNotifier::napsJobBlocked(GenerateSaftJob::class, 'saft:empresa:2024', 'locked-by-worker-3');

// Job dispatch blocked
CliNotifier::jobDispatchBlocked(
    GenerateSaftJob::class, 
    'saft:empresa:2024', 
    'priority',
    ['scheduled_for' => '2024-01-15 10:00:00']
);
```

### Notificações de Email

```php
CliNotifier::emailSendingAttempt('invoice', 'cliente@email.com', ['invoice_id' => 123]);
CliNotifier::emailSentSuccessfully('invoice', 'cliente@email.com');
CliNotifier::emailSendingFailed('invoice', 'cliente@email.com', 'SMTP timeout');
CliNotifier::emailServiceUnavailable('smtp.provider.com', 'Connection refused');
```

### Notificações de Sistema

```php
// Command execution
CliNotifier::commandCompleted('migrate:fresh', 2500.5, 0);
CliNotifier::commandFailed('queue:work', 'Out of memory', 1500.0);

// System health
CliNotifier::systemHealth([
    'memory' => '256MB',
    'load' => 0.5,
    'disk' => '50GB',
    'uptime' => '5 days'
]);
```

---

## Service Provider

O `NotificationServiceProvider` regista todos os componentes no container DI:

```php
<?php

namespace Og\Modules\Common\Notifications\Providers;

use Og\Modules\Common\Notifications\Channels\DatabaseChannel;
use Og\Modules\Common\Notifications\Channels\EmailChannel;
use Og\Modules\Common\Notifications\Channels\MattermostChannel;
use Og\Modules\Common\Notifications\Formatters\ContextFormatter;
use Og\Modules\Common\Notifications\Formatters\JobContextFormatter;
use Og\Modules\Common\Notifications\Formatters\StackTraceFormatter;
use Og\Modules\Common\Notifications\Services\NotificationService;
use Og\Modules\Common\Providers\ServiceProvider;

class NotificationServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(NotificationService::class, function ($app) {
            $debugEnabled = forceBoolean($app['app']->getParam('NOTIFICATION_DEBUG', true));
            $service = new NotificationService(isDebugEnabled: $debugEnabled);

            // Registar formatters
            $service->addFormatter('context', new ContextFormatter());
            $service->addFormatter('job_context', new JobContextFormatter());
            $service->addFormatter('exception', new StackTraceFormatter());

            // Registar canais base
            $service->addChannel(new DatabaseChannel($app->get('message-center')));
            $service->addChannel(new EmailChannel($app->get('email')));

            // Mattermost condicional
            if (config('notifications.channels.mattermost.enabled', false)) {
                $service->addChannel(
                    new MattermostChannel(
                        config('notifications.channels.mattermost.api_url'),
                        config('notifications.channels.mattermost.token'),
                        config('notifications.channels.mattermost.channel_id')
                    )
                );
            }

            return $service;
        });
    }
}
```

---

## Fluxo de Notificação

```mermaid
sequenceDiagram
    participant Client
    participant Facade as Notification Facade
    participant Service as NotificationService
    participant Channel1 as DatabaseChannel
    participant Channel2 as MattermostChannel
    
    Client->>Facade: using(Mattermost)->send(data)
    Facade->>Service: using(Mattermost)
    Service->>Service: selectedChannels = [Mattermost]
    Facade->>Service: send(data)
    
    alt isDebugging && !debugEnabled
        Service-->>Client: [] (skip)
    else
        loop For each channel
            Service->>Service: shouldUseChannel(channel)?
            alt Channel selected
                Service->>Channel2: send(notification)
                Channel2-->>Service: true/false
            end
        end
        Service->>Service: resetState()
        Service-->>Client: results[]
    end
```

---

## Extensibilidade

### Criar Novo Canal

```php
<?php

namespace App\Notifications\Channels;

use Og\Modules\Common\Notifications\Contracts\NotificationChannelInterface;

class SlackChannel implements NotificationChannelInterface
{
    public function __construct(
        private string $webhookUrl,
        private string $defaultChannel = '#general'
    ) {}

    public function send(array $notification): bool
    {
        try {
            $client = new \GuzzleHttp\Client();
            $response = $client->post($this->webhookUrl, [
                'json' => [
                    'channel' => $notification['slack_channel'] ?? $this->defaultChannel,
                    'text' => $notification['text'],
                    'attachments' => [
                        [
                            'color' => $this->getColor($notification['color'] ?? 'good'),
                            'title' => $notification['subject'] ?? '',
                            'text' => $notification['formatted_text'] ?? '',
                        ]
                    ]
                ]
            ]);
            return $response->getStatusCode() === 200;
        } catch (\Throwable $e) {
            return false;
        }
    }

    public function shouldSend(array $notification): bool
    {
        return ($notification['slack'] ?? false) === true;
    }

    private function getColor(string $type): string
    {
        return match ($type) {
            'error', 'danger' => 'danger',
            'warning' => 'warning',
            default => 'good'
        };
    }
}
```

**Registar no Provider:**

```php
$service->addChannel(new SlackChannel(
    config('notifications.channels.slack.webhook_url')
));
```

### Criar Novo Formatter

```php
<?php

namespace App\Notifications\Formatters;

use Og\Modules\Common\Notifications\Contracts\FormatterInterface;

class JsonFormatter implements FormatterInterface
{
    public function format($data): string
    {
        if (!is_array($data) && !is_object($data)) {
            return '';
        }
        
        return "```json\n" . json_encode($data, JSON_PRETTY_PRINT) . "\n```";
    }
}
```

**Registar no Provider:**

```php
$service->addFormatter('json', new JsonFormatter());
```

**Usar:**

```php
$formatted = $service->formatData([
    'json' => ['user' => ['id' => 1, 'name' => 'João']]
]);
```

---

## Referência de Ficheiros

| Componente | Caminho |
|------------|---------|
| Facade | [Notification.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Facades/Notification.php) |
| Service | [NotificationService.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Services/NotificationService.php) |
| DatabaseChannel | [DatabaseChannel.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Channels/DatabaseChannel.php) |
| EmailChannel | [EmailChannel.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Channels/EmailChannel.php) |
| MattermostChannel | [MattermostChannel.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Channels/MattermostChannel.php) |
| ContextFormatter | [ContextFormatter.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Formatters/ContextFormatter.php) |
| JobContextFormatter | [JobContextFormatter.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Formatters/JobContextFormatter.php) |
| StackTraceFormatter | [StackTraceFormatter.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Formatters/StackTraceFormatter.php) |
| CliNotifier | [CliNotifier.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/EnvironmentNotifiers/CliNotifier.php) |
| ServiceProvider | [NotificationServiceProvider.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Notifications/Providers/NotificationServiceProvider.php) |
| Config | [notifications.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Config/notifications.php) |
| Helper notify() | [Helpers.php#L540](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Helpers/Helpers.php#L540) |

---

## Conclusão

O Sistema de Notificações do OfficeGest oferece:

- **Flexibilidade**: Múltiplos canais com selecção dinâmica
- **Extensibilidade**: Fácil adição de novos canais e formatadores
- **Debugging**: CliNotifier com 40+ métodos especializados para monitorização
- **Compatibilidade**: Suporte a formato legacy e novo nas `options`
- **Performance**: Singleton service com reset automático de estado
- **Segurança**: Silent fail em canais não afecta outros canais