## CC-4 — Gerar e compartilhar o link de convite

**PRD:** `docs/prd/criacao-e-convite.md`

**Como** administrador, **quero** um link único do bolão para jogar no grupo
do WhatsApp, **para** chamar 20 pessoas de uma vez em vez de cadastrar
e-mail por e-mail.

**Prioridade:** Must — cadastrar convidado um por um é trabalho que o admin
não vai fazer; sem o link, o bolão não sai do papel.

**Critérios de aceite:**

- Dado que sou admin do bolão, quando peço o convite, então recebo um link
  único daquele bolão, pronto para compartilhar.
- Dado que tenho o link, quando uso a ação de compartilhar, então consigo
  mandá-lo pelos apps do device (WhatsApp incluído).
- Dado que o link caiu no lugar errado, quando eu o invalido, então ele deixa
  de dar acesso e eu posso gerar outro.
- Dado que sou participante comum, quando tento gerar ou invalidar o convite,
  então a requisição é recusada.
- Dado que o bolão foi encerrado, quando alguém usa o link, então o acesso é
  recusado.
- Dado que o primeiro jogo do bolão **já começou**, quando peço o convite,
  então a geração é recusada com o motivo — e não recebo um código que nunca
  vai funcionar. Descoberto em 06/08 na verificação em device: o app entregava
  o código, o admin mandava no grupo e quem descobria o erro era o convidado.
- Dado que a geração é recusada, então o app mostra o motivo, não um erro
  genérico.

**Tasks:**

_Backend_
- [x] `BE-1` Gerar token de convite por bolão, não adivinhável (depende de CC-1 BE-2)
- [x] `BE-2` Endpoint de invalidação e regeração do convite, com autorização de admin (depende de BE-1)
- [x] `BE-3` Recusar convite de bolão encerrado (depende de BE-1)
- [x] `BE-4` Recusar **geração** de convite em bolão que já começou, com a mesma regra da entrada (depende de BE-1)

_Mobile_
- [x] `FE-1` Tela de convite com o link e ação de compartilhar do sistema (depende de BE-1)
- [x] `FE-2` Ação de invalidar e gerar novo link, visível só para admin (depende de BE-2)

_QA_
- [x] `QA-1` Escrever o plano de casos de teste a partir desta story
- [x] `QA-2` Executar o plano com link invalidado, bolão encerrado, bolão já começado e tentativa como participante comum

**Riscos/Dependências:**

- **O link é o controle de acesso ao bolão.** Token adivinhável deixa
  estranho entrar num bolão fechado.
- Depende de CC-1; é pré-requisito de CC-5.
- **Decidido em 03/08: o link expira em 1 dia.** Passado isso, o admin gera
  outro (`BE-2`, `FE-2`). ⚠️ Atinge a persona mais frágil: o casual da Fase 1
  disse que "deixa pra depois", e quem lê a mensagem no dia seguinte não
  entra. Isso devolve ao admin o trabalho manual de regerar link — o oposto
  do objetivo do épico. **Vale instrumentar quantos convites são regerados**;
  se for muito, revisar o prazo.

- **06/08, verificado em device:** dois defeitos corrigidos aqui. (1) Abrir a
  tela de convite **gerava** um código, e gerar revoga o anterior — o admin que
  abrisse para conferir matava o link que tinha acabado de mandar no grupo. A
  geração passou a ser ação explícita, com confirmação avisando que o atual
  morre. (2) A ação de convite aparecia para participante comum, que só
  descobriria o 403 ao tocar.
- O código já gerado é guardado **no device** (`ConviteStore`), porque o
  servidor guarda só o hash e não consegue devolvê-lo. Consequência aceita: em
  outro aparelho ou depois de reinstalar, o admin só pode gerar um novo.

- **06/08 — QA:** plano em `docs/testes/CC-4-plano.md`, execução em
  `docs/testes/CC-4-execucao.md` (**14/14 ✅**). Dois casos de servidor que
  ninguém cobria (token próprio por bolão; invalidar sem convite ativo) passaram
  sem alteração.
- **06/08 — defeito alto encontrado e corrigido:** o app compartilha um **link**
  e o diálogo de entrada pede "o código". Quem recebe no WhatsApp copia o link e
  cola — e o servidor respondia **"convite inválido ou já substituído"**:
  diagnóstico errado sobre convite válido, no ponto onde o convidado desiste.
  Corrigido em `lib/features/boloes/data/token_de_convite.dart` (link, link no
  meio de frase, espaço, query anexada, percent-encoding). Verificado em device:
  link inteiro colado → entrou no bolão e caiu na tela de palpite com as regras.
  Nenhum teste pegava isso porque todos colavam o token limpo — que é o que o
  **teste** tem em mão, não o que a pessoa tem.
