# Guia de Integração — Módulo OTP

> Caminho do módulo: `componentes/Missoes/Mensageria/sinais/Otp/`
> Namespace: `GalaxiaMissoes\Mensageria\sinais\Otp`

Este guia mostra como qualquer outro sinal pode consumir o módulo OTP para validar identidade/intenção do usuário via código one-time. Ao final há um exemplo *ilustrativo* (alteração de senha por e-mail) que **não está implementado** — serve só como referência de uso.

---

## 1. Quando usar este módulo

Use o OTP genérico em qualquer fluxo que precise validar identidade/intenção de um usuário através de um código de uso único:

- Aprovação de operação sensível em ambientes logados.
- Confirmação de canal (e-mail, telefone) durante cadastro.
- Autorização temporária de acesso público sem login persistente (caso atual: edição de rota pelo motorista).
- Reset/troca de credencial.

**Não use** este módulo para autenticação de longo prazo (sessão, JWT), 2FA contínuo (TOTP, hardware key) ou MFA com fator inerente (biometria). Esses pertencem a outros módulos.

---

## 2. Fluxo padrão de integração

A integração é feita em quatro passos no bootstrap do consumidor + chamadas ao `OtpSatelite` no fluxo:

1. **Definir um `contexto` único** — string snake_case, ≤50 chars, globalmente única no sistema (ex.: `edicao_rota`, `alteracao_senha`).
2. **Registrar `OtpConfig`** com validade, tentativas, tamanho do código e canal padrão para esse contexto.
3. **(Opcional) Implementar `OtpEventoBase`** se o consumidor precisa reagir à validação/expiração/cancelamento (ex.: abrir sessão, alterar registro próprio).
4. **Registrar o evento** via `OtpEventoRegistry::registrar($contexto, $eventoClass)`.
5. **Chamar o `OtpSatelite`** com `'criar_e_enviar'`, `'validar'`, `'cancelar'` ou `'consultar'` ao longo do fluxo.

A única dependência pública estável é o `OtpSatelite`. Laboratórios podem mudar de assinatura entre PRs — o satélite garante compatibilidade.

---

## 3. Exemplo ilustrativo (não implementado): alteração de senha por e-mail

```php
// ===========================================================
// 1) Bootstrap do sinal AlteracaoSenha (em seu App.php)
// ===========================================================

use GalaxiaMissoes\Mensageria\sinais\Otp\Configuracao\OtpConfig;
use GalaxiaMissoes\Mensageria\sinais\Otp\Eventos\OtpEventoRegistry;

OtpConfig::registrar('alteracao_senha', [
    'validade_segundos' => 600,    // 10 min
    'max_tentativas'    => 3,
    'tamanho_codigo'    => 6,
    'canal_padrao'      => 'email',
]);
OtpEventoRegistry::registrar(
    'alteracao_senha',
    AlteracaoSenhaOtpEvento::class
);

// ===========================================================
// 2) Evento próprio do consumidor (Eventos/AlteracaoSenhaOtpEvento.php)
// ===========================================================

use GalaxiaMissoes\Mensageria\sinais\Otp\Eventos\OtpEventoBase;

class AlteracaoSenhaOtpEvento extends OtpEventoBase
{
    public function aoValidar(object $otp, array $extras = []): void
    {
        // Aqui o consumidor faz o efeito colateral próprio.
        // Tipicamente: aplicar a nova senha já guardada como rascunho.
        $usuarioId = (int) $otp->referencia_externa;
        $novaSenha = $extras['nova_senha_hash'] ?? null;
        if ($novaSenha) {
            (new UsuarioSonda())
                ->make(['id' => $usuarioId, 'senha_hash' => $novaSenha])
                ->save();
        }
    }
}

// ===========================================================
// 3) Quando o usuário clica "Quero trocar senha"
// ===========================================================

use GalaxiaMissoes\Mensageria\sinais\Otp\Satelites\OtpSatelite;

$resp = (new OtpSatelite($galaxia, 'criar_e_enviar', [
    'contexto'           => 'alteracao_senha',
    'referencia_externa' => $usuarioId,
    'destinatario'       => $usuario->email,
    'dados_adicionais'   => ['solicitado_em' => date('c')],
]))->response;
// $resp = ['notification_class' => 'success', 'otp_id' => 42, 'status' => 'enviado', 'expira_em' => '...']

// ===========================================================
// 4) Quando o usuário envia código + nova senha
// ===========================================================

$resp = (new OtpSatelite($galaxia, 'validar', [
    'contexto'           => 'alteracao_senha',
    'referencia_externa' => $usuarioId,
    'codigo'             => $request['codigo'],
    'extras'             => [
        'nova_senha_hash' => password_hash($request['nova_senha'], PASSWORD_BCRYPT),
        'ip'              => $_SERVER['REMOTE_ADDR'] ?? null,
    ],
]))->response;

if (($resp['notification_class'] ?? '') === 'success') {
    // AlteracaoSenhaOtpEvento::aoValidar já foi chamado — internamente
    // ele aplicou a nova senha. Aqui só responde ao front.
    echo "Senha alterada.";
} else {
    // motivo é tipado — vide §4
    echo "Falha: {$resp['motivo']} — {$resp['notification_text']}";
}
```

> ⚠️ Esse exemplo é ilustrativo. Não há implementação de `AlteracaoSenha` no projeto e nenhum canal `email` está registrado por padrão.

---

## 4. Códigos de erro retornados em `validar`

A resposta de `validar` traz um `motivo` tipado quando não é sucesso. O front pode usá-lo para escolher mensagem ou caminho de UI:

| `motivo`                | Significado                                                | Ação sugerida ao front                                    |
|-------------------------|------------------------------------------------------------|-----------------------------------------------------------|
| `sem_otp_ativo`         | Não há OTP gerado/ativo para o par (contexto, ref).        | Mostrar "Solicite um novo código".                        |
| `expirado`              | OTP expirou antes da validação.                            | Mostrar "Código expirado" e oferecer reenvio.             |
| `cancelado`             | OTP foi cancelado (manualmente ou por novo OTP gerado).    | Mesma UI de expirado: oferecer novo código.               |
| `ja_validado`           | OTP já consumido com sucesso anteriormente.                | Encaminhar para próxima etapa do fluxo.                   |
| `codigo_incorreto`      | Código digitado não bate. Resposta inclui `tentativas_restantes`. | Mostrar erro e contagem regressiva de tentativas.    |
| `tentativas_excedidas`  | Excedeu `max_tentativas`. OTP fica em estado final.        | Forçar geração de novo código.                            |
| `transicao_invalida`    | OTP em estado que não aceita validação (ex.: aguardando envio). | Mensagem de erro genérico; investigar (raro).        |
| `validacao_falhou`      | Payload incompleto (validator).                            | Validar formulário antes de enviar.                       |
| `erro_interno`          | Exceção não tratada no laboratório.                        | Mostrar erro genérico; logs no servidor.                  |

---

## 5. Lista de canais disponíveis

| Canal | Identificador | Status                                                        |
|-------|---------------|---------------------------------------------------------------|
| Log   | `log`         | Disponível por padrão. Escreve em `error_log`. Útil em dev/test. |
| SMS   | `sms`         | Não implementado. Provider real é PR próprio.                 |
| E-mail| `email`       | Não implementado. Provider real é PR próprio.                 |
| WhatsApp | `whatsapp` | Não implementado. Provider real é PR próprio.                 |

Para registrar um provider real:

```php
use GalaxiaMissoes\Mensageria\sinais\Otp\Canais\OtpCanalRegistry;

OtpCanalRegistry::registrar('sms', \Vendor\SmsTwilioCanal::class);
```

A classe deve implementar `OtpCanalInterface`. Registry valida no momento do `registrar`.

---

## 6. Convenções para nomear `contexto`

- `snake_case`, ASCII, ≤50 caracteres, único globalmente.
- Refletir o domínio (ex.: `edicao_rota`, `alteracao_senha`, `cadastro_usuario`).
- **Não** prefixar com módulo (`mensageria_alteracao_senha`) — o contexto é a chave entre consumidor e OTP, não pertence ao OTP.
- Versão se mudar a semântica: `alteracao_senha_v2`.

---

## 7. Onde NÃO usar este módulo

- **Sessões persistentes / JWT / cookies de longo prazo** — use o módulo de autenticação principal.
- **2FA contínuo (TOTP / Yubikey)** — escopo do módulo `Verificacao/sinais/Mfa` (futuro).
- **CAPTCHA / proof-of-work** — escopo de outro módulo.
- **Validação de e-mail/telefone como atributo permanente do usuário** — pode usar OTP para *verificar*, mas o estado "verificado" deve viver no domínio do usuário, não no OTP (que é efêmero).

---

## 8. Limitações conhecidas (v1)

- **Sem rate-limit por IP/usuário.** O único limite é `max_tentativas` por OTP. Para proteger contra abuso de criação massiva, o consumidor deve aplicar throttling antes de chamar `criar_e_enviar`.
- **Sem retry automático em falha de envio.** Se o canal falhar, o consumidor decide se chama `enviar`/`reenviar` novamente.
- **Um evento por contexto.** Se duas features compartilham contexto, criar contextos distintos.
- **Sem job de limpeza.** Registros expirados/cancelados ficam na tabela. O índice em `expira_em` permite job futuro de purga.
- **Sem suporte a múltiplos OTPs simultâneos para o mesmo par (contexto, referência).** Gerar um novo OTP cancela o anterior automaticamente.

---

## 9. Referências internas

- PRD do módulo: [`PRD.md`](PRD.md)
- Tasks operacionais: [`tasks/`](tasks/)
- Exemplo real de consumidor: `componentes/Missoes/Logistica/sinais/EdicaoRota/Eventos/EdicaoRotaOtpEvento.php`
