# 06 — Modello dati (bozza concettuale)

> Bozza **concettuale**. Separa la parte **stabile** (procedibile ora) dalla parte **dipendente
> dalla formula** ([`04-riforma-formula.md`](04-riforma-formula.md)).
> DB scelto: **Neon Postgres + Drizzle** (vedi [`decisioni/2026-06-07-stack-architettura.md`](decisioni/2026-06-07-stack-architettura.md))
> → i campi `format_config`/`config` sono **JSONB**; classifiche/ranking sfruttano window functions.

## Entità stabili (procedibili a prescindere dalla formula)

### User / Account
- `id`, `email`, `password_hash`, `ruolo` (player|admin|superadmin), `created_at`, consensi GDPR.

### Player (anagrafica giocatore)
- `id`, `user_id` (nullable: storici non registrati), **`nick_tdg`** (forum Tana dei Goblin),
  **`nome_app_tta`** (nickname nell'app CGE), `attivo`, `note`.
- ⚠️ `nick_tdg` e `nome_app_tta` sono **distinti** (vedi `../tasks/lessons.md`).

### Event / Competizione
- `id`, `tipo` (set **aperto/estendibile**: BOLGIA, TORNEO_GENERALE, DUELLO, PALIO, SUPERCOPPA, …, CUSTOM),
  `identificativo_stagione` (es. `EST26`, `AUT25`, `DUEL25`), `anno`, `nome`,
  `data_inizio`, `data_fine`, `stato` (bozza|iscrizioni|in_corso|concluso), `regolamento_versione`,
  **`format_config`** (JSON: la configurazione completa del formato — vedi sotto).

### Phase (fase di un evento) — abilita eventi compositi
- `id`, `event_id`, `ordine`, `tipo_fase` (GIRONI_ROUND_ROBIN | BRACKET_ELIMINAZIONE | A_SQUADRE |
  PARTITA_SINGOLA), `config` (JSON), `regola_avanzamento` (chi passa alla fase successiva).
- Un **Torneo Generale** = fase gironi → fase eliminazione; un **Duello** = una fase bracket;
  una **Bolgia** = una fase gironi round-robin con ranking; una **Supercoppa** = partita singola.
  Le fasi si **concatenano** (output di una alimenta il seeding della successiva).

### Match / Partita
- `id`, `event_id`, `girone_id` (nullable), `n_giocatori` (2|3|4), `data_avvio_prevista`,
  `data_avvio_effettiva`, `stato` (programmata|in_corso|conclusa|resign|timeout), `link_app_cge`.

### MatchParticipant (riga giocatore in partita)
- `id`, `match_id`, `player_id` (o `is_bot`), `posizione` (1..N), `punti_cultura`,
  `punti_torneo`, `resign` (bool).

### Standing / Classifica (riga di classifica in un girone/evento)
- `id`, `event_id`, `girone_id`, `player_id`, `punti_torneo`, `punti_spareggio`, `esito`.

### Ranking (storico persistente cross-stagione)
- `id`, `player_id`, `identificativo_stagione`, `serie`, `posizione_globale`, `note` (amm./sql./rit.).
- Modella il foglio "Andamento Ranking" (~177 giocatori).

### Sanction / Sanzione
- `id`, `player_id`, `event_id`, `tipo` (timeout|ammonizione|squalifica|penalizzazione|ban),
  `valore` (es. −5 posizioni, −1 punto), `giustificato` (bool), `note`.

### Notification
- `id`, `user_id`, `tipo`, `payload`, `canale` (email|push), `inviata_at`, `letta_at`.

### Content / Notizia (vetrina)
- `id`, `titolo`, `corpo`, `pubblicata_at`, `autore_id`, `tipo` (notizia|pagina).

## Configurazione del formato — il MOTORE GENERICO (niente logica hardcoded)

> Tutto ciò che segue è **dato di configurazione** (`format_config` / `Phase.config`), non codice
> specifico per Bolgia. La formula indecisa (13/7/4) è quindi solo un valore qui dentro: l'engine
> resta lo stesso. Vedi il principio in [`05-requisiti-prodotto.md`](05-requisiti-prodotto.md).

### Girone / Serie
- `id`, `event_id`, `phase_id`, `livello` (A, B1, B2, …, H), `nome_tematico`,
  `dimensione` (**configurabile**: es. 4 / 7 / 13), `ordine`.
- Dimensione e numero di gironi sono **parametri** dell'evento, non costanti.

### Regole di formato (configurazione per tipo evento / fase / dimensione girone)
- **Tabella punteggi** per numero giocatori: 2→(4,0); 3→(10,4,0); 4→(11,6,2,0). *(stabile oggi,
  ma da confermare se cambia la formula)*
- **Composizione partite** per stagione: oggi 13 = (6×3p + 4×4p); 7 = (6×3p); 4 = (3×2p+3×3p+2×4p).
- **Movimenti promo/retro** per livello (es. gironi-4: +2/+1/−1/−2), con varianti "inverno".
- **Multipli ottimali** e gestione Coda/H (oggi 52/65/77).

## Algoritmi chiave (parametrizzati dalla configurazione, non hardcoded)
1. **Seeding & generazione gironi/bracket** dal ranking globale + iscritti + Coda/H.
2. **Generazione calendario partite** (round-robin doppio / schema custom).
3. **Calcolo classifica + spareggi** (punti torneo → punti spareggio = % cultura vs vincitore;
   resign = 0,5).
4. **Applicazione promo/retro + sanzioni** → nuovo ranking della stagione successiva.

## Note di migrazione
Lo storico (26 fogli xlsx) popola: `Player`, `Event`, `Girone`, `Standing`, `Ranking`, albo d'oro.
Vedi [`../data/README.md`](../data/README.md).
