# 🏆 Quiniela MB — Roadmap

> App de quiniela mundialista para el equipo de trabajo. Los jugadores compran
> "quinielas" (boletos), pronostican los partidos del torneo y compiten por el
> **Jackpot** (la suma del valor de TODAS las quinielas jugadas). El administrador
> carga los resultados manualmente por fase para no depender de APIs de pago.

---

## 0. Estado de avance

> **Implementado (MVP completo + reparto):** Fases 0 → 8. La app ya corre con
> registro/login, panel de admin, compra y validación de quinielas, jackpot en
> vivo, pronósticos con deadline, carga manual de resultados con motor de
> puntuación, tabla de posiciones y cálculo de reparto del premio.
> Ver instrucciones en [`README.md`](./README.md).
>
> **Pendiente:** Fase 9 (pulido/seguridad/despliegue) y Fase 10 (mejoras futuras).

| Fase | Estado |
|---|---|
| 0 · Setup | ✅ Hecho |
| 1 · Auth y usuarios | ✅ Hecho |
| 2 · Torneo y configuración | ✅ Hecho |
| 3 · Fases y partidos | ✅ Hecho |
| 4 · Compra y validación + jackpot | ✅ Hecho |
| 5 · Pronósticos con deadline | ✅ Hecho |
| 6 · Resultados + puntuación | ✅ Hecho |
| 7 · Leaderboard | ✅ Hecho |
| 8 · Cierre y reparto | ✅ Hecho |
| 9 · Pulido y despliegue | ⏳ Pendiente |
| 10 · Mejoras futuras | ⏳ Pendiente |

---

## 1. Stack y decisiones de producto

| Decisión | Elección |
|---|---|
| **Stack** | Node.js + Express + EJS (vistas server-rendered) + SQLite |
| **Puntaje** | Marcador exacto (más puntos) + acertar ganador 1/X/2 (menos puntos) |
| **Premio** | Esquema de reparto **configurable por el admin** (con % de comisión/casa opcional) |
| **Registro/Login** | Email + contraseña (sesiones); el admin valida los pagos |
| **Resultados** | Carga **manual** por el admin, por partido/fase. Sin APIs externas. |

### Stack técnico detallado
- **Runtime:** Node.js (LTS) + Express.
- **Vistas:** EJS + layout simple (puede ser con un poco de CSS propio o Bootstrap/Tailwind por CDN).
- **Base de datos:** SQLite (archivo local `quinielamb.db`), vía `better-sqlite3` (síncrono, sencillo) o `knex`/`sequelize` si se prefiere migraciones formales. **Sugerencia: `better-sqlite3` + migraciones manuales en SQL.**
- **Sesiones/Auth:** `express-session` + `bcrypt` para hashear contraseñas. (Sin OAuth.)
- **Validación:** `express-validator` o validación manual.
- **Seguridad:** `helmet`, `csurf` (CSRF en formularios), rate-limit en login.
- **Dev:** `nodemon`, `dotenv` para configuración.

---

## 2. Conceptos del dominio (glosario)

- **Torneo / Evento:** el Mundial (o cualquier evento). Define precio por quiniela, fechas, esquema de reparto y comisión. Puede haber uno activo a la vez (o varios históricos).
- **Fase:** etapa del torneo (Fase de grupos J1, J2, J3, Octavos, Cuartos, Semis, Final). Sirve para organizar partidos y cargar resultados por bloque.
- **Partido:** local vs visitante, fecha/hora de inicio (= **deadline** de pronóstico), y resultado real (lo carga el admin).
- **Quiniela (boleto):** una entrada comprada por un jugador. Se nombra automáticamente `Quiniela N de {Nombre}` (Quiniela 1 de Rómulo, Quiniela 2 de Rómulo…). Cada boleto es **independiente** y acumula sus propios puntos → un jugador con 2 quinielas puede pronosticar distinto en cada una.
- **Pronóstico:** el marcador que un jugador predice para un partido, **dentro de una quiniela concreta**.
- **Jackpot:** suma del `monto` de **todas las quinielas activas** del torneo. Se recalcula cada vez que el admin valida una compra.
- **Puntos:** los acumula cada quiniela según aciertos. Determinan el ranking y el reparto del jackpot.

---

## 3. Modelo de datos (tablas)

```
usuarios
  id, nombre, email (único), password_hash, rol ('admin' | 'jugador'),
  activo, creado_en

torneos
  id, nombre, precio_quiniela, moneda ('USD'),
  pts_marcador_exacto (def. 3), pts_ganador (def. 1),
  comision_pct (def. 0), estado ('config' | 'abierto' | 'en_curso' | 'cerrado'),
  creado_en

reparto_premios            -- esquema configurable por torneo
  id, torneo_id, posicion (1,2,3...), porcentaje

fases
  id, torneo_id, nombre ('Grupos J1', 'Octavos'...), orden

partidos
  id, torneo_id, fase_id, equipo_local, equipo_visitante,
  inicia_en (deadline), goles_local, goles_visitante,
  estado ('programado' | 'cerrado' | 'finalizado')

quinielas                  -- los "boletos"
  id, torneo_id, usuario_id, numero (1,2,3 por usuario en ese torneo),
  nombre ('Quiniela 1 de Rómulo'), monto,
  estado ('pendiente_pago' | 'activa' | 'anulada'),
  validada_por (admin_id), validada_en, creado_en

pronosticos
  id, quiniela_id, partido_id, pred_local, pred_visitante,
  puntos_obtenidos (def. 0), actualizado_en
  -- UNIQUE(quiniela_id, partido_id)
```

**Cálculo del jackpot** = `SELECT SUM(monto) FROM quinielas WHERE torneo_id=? AND estado='activa'`.

---

## 4. Reglas de negocio clave

1. **Compra de quiniela:** el jugador solicita comprar N quinielas → quedan en `pendiente_pago`. El admin confirma el pago offline y las pasa a `activa` ("le da el punto/crédito"). Solo las `activa` cuentan para el jackpot y el ranking.
2. **Numeración:** al validar, se asigna `numero` correlativo por usuario y torneo → genera el nombre `Quiniela N de {Nombre}`.
3. **Deadline de pronósticos:** un pronóstico solo se puede crear/editar **antes de `partidos.inicia_en`**. Al iniciar el partido, queda bloqueado.
4. **Independencia de boletos:** cada quiniela tiene su propio set de pronósticos y su propio puntaje.
5. **Motor de puntuación** (al cargar un resultado):
   - **Marcador exacto** (`pred_local == goles_local` y `pred_visitante == goles_visitante`) → `pts_marcador_exacto`.
   - **Acertó ganador/empate** (signo del resultado correcto, marcador no exacto) → `pts_ganador`.
   - **Falló** → 0.
   - El recálculo es **idempotente**: recargar/corregir un resultado recalcula limpio los puntos de ese partido para todas las quinielas.
6. **Ranking:** suma de `puntos_obtenidos` por quiniela, ordenado desc. Desempates: (a) más marcadores exactos, (b) fecha de compra más antigua (configurable).
7. **Cierre y reparto:** el admin cierra el torneo → el sistema toma el jackpot, descuenta `comision_pct`, y reparte el resto según `reparto_premios` (ej. 60/30/10%). Muestra el desglose de cuánto le toca a cada quiniela ganadora.

---

## 5. Roles y pantallas

### Jugador
- Registro / login (email + contraseña).
- Dashboard: sus quinielas, estado de cada una, puntos y posición.
- Comprar quiniela(s) → instrucciones de pago + queda pendiente de validación.
- Ingresar/editar pronósticos por quiniela (solo partidos abiertos).
- Ver tabla de posiciones (leaderboard) y el jackpot actual.

### Administrador
- Panel: crear/editar torneo (precio, puntos, comisión, esquema de reparto).
- Cargar fases y partidos (fixtures) con fecha/hora.
- **Validar pagos:** ver compras pendientes y activarlas.
- **Cargar resultados** por partido/fase → dispara recálculo de puntos.
- Ver jackpot, ranking global y desglose por jugador.
- Cerrar torneo y generar el reparto del premio.

---

## 6. Roadmap por fases de desarrollo

> Cada fase es un entregable funcional. El MVP llega hasta la Fase 7.

### Fase 0 — Setup del proyecto
- Estructura de carpetas (`/src`, `/views`, `/public`, `/db`).
- Express + EJS + layout base, `.env`, scripts npm, `nodemon`.
- Conexión SQLite + script de migración inicial (crea tablas) + seed de un usuario admin.

### Fase 1 — Autenticación y usuarios
- Registro (email + contraseña con `bcrypt`), login, logout, sesiones.
- Middleware `requireAuth` y `requireAdmin`.
- Protección CSRF en formularios y rate-limit en login.

### Fase 2 — Torneo y configuración (admin)
- CRUD de torneo: precio por quiniela, puntos (exacto/ganador), comisión.
- Configuración del esquema de reparto (posiciones + %).
- Estados del torneo (config → abierto → en curso → cerrado).

### Fase 3 — Fases y partidos (admin)
- CRUD de fases y partidos (local, visitante, fecha/hora).
- Carga de fixtures de forma cómoda (formulario por fase).

### Fase 4 — Compra y validación de quinielas
- Jugador solicita N quinielas → `pendiente_pago`.
- Admin valida → asigna número, nombre `Quiniela N de X`, estado `activa`.
- **Cálculo y visualización del Jackpot** en vivo.

### Fase 5 — Pronósticos del jugador
- Formulario de pronóstico por quiniela y partido, con bloqueo por deadline.
- Vista clara de qué falta pronosticar y qué ya está cerrado.

### Fase 6 — Carga de resultados + motor de puntuación
- Admin carga resultado de cada partido (o en bloque por fase).
- Recálculo idempotente de `puntos_obtenidos` de todas las quinielas afectadas.

### Fase 7 — Ranking / Leaderboard  ✅ *(fin del MVP)*
- Tabla de posiciones por torneo (puntos por quiniela), con desempates.
- Vista pública para el equipo + vista detalle por quiniela.

### Fase 8 — Cierre y reparto del Jackpot
- Cierre del torneo → cálculo de comisión + reparto según esquema.
- Pantalla de "ganadores" con el monto que le toca a cada quiniela.

### Fase 9 — Pulido y despliegue
- Mejoras de UI/UX, mensajes de error, responsive.
- Endurecimiento de seguridad (validaciones, headers, sesiones).
- Backup del archivo SQLite + script de respaldo.
- Despliegue (Railway / Render / VPS) + variables de entorno.

### Fase 10 — Mejoras futuras (opcional)
- Notificaciones por email (recordatorio de deadline).
- Importar fixtures por CSV.
- Predicciones especiales con bonos (campeón, goleador).
- Historial de torneos y estadísticas por jugador.
- Exportar resultados a PDF/imagen para compartir.

---

## 7. Sugerencia de MVP (primer corte usable)

Para tener algo funcionando rápido con el equipo, prioriza:
**Fase 0 → 1 → 2 (mínimo) → 3 → 4 → 5 → 6 → 7.**
Con eso ya se puede: registrar jugadores, vender/validar quinielas, ver el jackpot,
pronosticar, cargar resultados y ver el ranking. El reparto (Fase 8) se puede hacer
manualmente al inicio y automatizar después.

---

## 8. Riesgos / decisiones a vigilar
- **Zona horaria** de los deadlines (definir TZ del equipo y guardar en UTC).
- **Pagos offline:** dejar registro de quién validó y cuándo (auditoría simple).
- **Backups de SQLite:** un archivo = un solo punto de falla; respaldar seguido.
- **Edición de resultados ya cargados:** permitir corrección con recálculo seguro.
- **Concurrencia baja** (equipo pequeño) → SQLite es más que suficiente.
