# TheL4FPRO - Contexto para IA

> **Proyecto**: Plataforma de gestión para academia de fútbol (reservas, academia, usuarios, finanzas)
> **Stack**: FastAPI + SQLAlchemy + SQLite + Jinja2

---

## Arquitectura General

```
app/
├── main.py              # Entry point, lifespan, middleware, router includes
├── database.py          # Configuración BD, templates, static files, init_db()
├── models/base.py       # SQLAlchemy engine, SessionLocal, get_db()
├── models/user.py       # User + 7 tablas satélite (profile, sports, health, consent, guardians, marketing, finance)
├── models/reservation.py # Reservation, ReservationStudent, JugadorGasto
├── models/academy.py    # Course, Section, Lesson, Enrollment, Progress
├── auth/deps.py         # current_user, require_admin, require_docente_or_admin, CSRF, sessions
└── [módulos]/routes.py  # Cada módulo expone sus rutas (auth, academia, reservations, notas, etc.)
```

**Flujo de dependencias**: `main.py` → `models/base.py:get_db()` → `models/base.py:engine` → SQLite (`thel4footbal.db`)

---

## Modelos Clave (SQLAlchemy)

### User (`models/user.py`)
- **Campos**: `id, email, name, first_name, last_name, phone, password_hash, role, is_mvp90, is_temporary, created_at`
- **Roles**: `admin | docente | alumno`
- **Tablas satélite** (1:1, cascade delete): `UserProfile, UserSports, UserHealth, UserConsent, UserGuardians, UserMarketing, UserFinance`
- **Patrón**: Al crear usuario en `auth_crud.create_user()`, se inicializan todas las tablas satélite vía `_ensure_user_satellites()`.

### Reservation (`models/reservation.py`)
- **Campos**: `id, date, time, duration_minutes, price_cents, user_id, docente_id, status, paid, name, email, phone, notes`
- **Restricciones**: **NO hay restricciones** - las reservas siempre se pueden crear:
  - No se valida si el docente ya tiene una reserva a esa fecha/hora
  - No se requiere seleccionar un docente específico (puede ser null)
  - No se valida que el docente_id corresponda a un usuario con rol 'docente'
- **Relaciones**: 
  - `students` → `ReservationStudent` (muchos a muchos)
  - `gastos` → `JugadorGasto` (gastos asociados a la reserva)

### JugadorGasto (Finanzas)
- **Campos**: `id, date, user_id, reservation_id, amount_cents, category, concept, notes`
- **Uso**: Tracking de gastos por alumno y por reserva.

---

## Base de Datos (SQLite)

El proyecto utiliza SQLite como base de datos, una solución ligera que no requiere configuración adicional ni servicios externos.

- **Archivo de Base de Datos**: `thel4footbal.db` (se crea automáticamente en la raíz del proyecto)
- **Conexión (DATABASE_URL)**: Por defecto, está preconfigurada en el código y en el archivo `.env` como:
  `sqlite:///thel4footbal.db`
- **Backups y Restauración** (Solo Administradores):
  - **Visibilidad**: La sección de copias de seguridad en `/reservas` solo es visible para usuarios con rol `admin`. Los docentes pueden acceder a la página de reservas pero no verán esta funcionalidad.
  - **Endpoints protegidos**: `/api/backup-db` y `/api/restore-db` requieren autenticación de administrador (`require_admin`).
  - **Backup**: Genera una copia del archivo `thel4footbal.db`.
  - **Restauración**: Permite subir un archivo `.db` y restaura la base de datos reemplazando el archivo actual.

---

## Autenticación & Autorización

### Session-based Auth (`auth/deps.py`)
```python
current_user(request)          # Devuelve sqlite3.Row / Row mapping o None
require_admin(request)         # 403 si no es admin + CSRF check
require_docente_or_admin(request)  # 403 si no es docente/admin
```

### CSRF Protection
- Token se genera en `rotate_session()` → `request.session["csrf"]`
- Se envía via header `X-CSRF-Token` en requests mutables (POST/PUT/PATCH/DELETE)
- Validación automática en `require_admin/require_docente_or_admin`

---

## Convenciones del Proyecto

### Acceso a datos de usuario
`current_user()` suele retornar un registro del motor raw. Por seguridad se estila accederlo como un diccionario intermedio:
```python
# En templates/routes
user["id"], user["role"], user["email"]

# Para verificar rol
role = (me["role"] if isinstance(me, object.__class__) else str(me.get("role", ""))) or ""
```

### Dependency Injection
Todas las rutas de la API utilizan: `db: Session = Depends(get_db)`

### Almacenamiento de Recursos (Imágenes, Archivos)
**REGLA CRÍTICA**: Todos los recursos como imágenes de perfil, portadas de cursos, avatares, documentos y cualquier otro dato binario **SIEMPRE deben guardarse en la base de datos** (como campos BLOB/base64 en las tablas correspondientes).

- **NUNCA** usar carpetas como `uploads/`, `static/uploads/`, o similares en el filesystem
- **NUNCA** guardar rutas de archivo en la BD que apunten a archivos en disco
- **Ventajas**: Backup completo con pg_dump, portabilidad entre entornos, consistencia de datos, sin problemas de permisos de filesystem

Ejemplo de campos típicos:
- `UserProfile.avatar` (imagen de perfil como base64 o bytes)
- `Course.cover_image` (portada del curso)
- `Lesson.attachments` (documentos asociados)

### Esquemas Genéricos de Formularios y Funciones
1. **Reservas**: Las rutas permiten generar reservas y asignarlas. Reciben `user_ids` (alumnos) para el tracking financiero.
2. **Matrículas / Pagos**: El saldo extra administrado en la API permite deducir costos individualmente para cada alumno.
3. **Usuarios Dummy/Temporales**: Las tarjetas temporales y MVP se marcan en la base de datos con los flags pertinentes para no interferir en el dashboard de estadísticas totales a menos que se solicite explícitamente.

---

## Estructura de URLs (Routers)

| Módulo | Prefijo | Rutas Clave |
|--------|---------|-------------|
| `auth.routes` | `/` | `/login`, `/register`, `/logout`, `/recuperar-password` |
| `academia.routes` | `/` | `/academy/*`, `/gestion-academia`, `/curso/{id}`, `/leccion/{id}` |
| `reservations.routes` | `/` | `/reservas` (dashboard general), `/api/reservations` |
| `notas.routes` | `/` | `/notas` |
| `notificaciones.routes` | `/` | `/notificaciones`, `/api/notifications` |
| `estadisticas.routes` | `/` | `/api/users`, `/api/users/{id}/role`, `/contabilidad` |
| `valoraciones.routes` | `/` | `/valoraciones` |

---

## Tareas Diarias / Comandos Frecuentes

```bash
# Iniciar servidor local
python3 run.py              # Ejecuta uvicorn app.main:app

# Poblar la Base de Datos base y perfiles dummy
python3 crear_usuarios.py

# Base de Datos SQLite
# El archivo thel4footbal.db se gestiona directamente
```

## Funcionalidades Críticas Estables (NO MODIFICAR)

Las siguientes funcionalidades han sido probadas extensivamente y funcionan perfectamente. Cualquier modificación debe ser evaluada cuidadosamente:

### Gestión de Reservas y Finanzas (Matrículas)
El sistema de reservas con gestión automática de saldos/matriculas está completamente operativo:

**Flujo de creación de reserva:**
```
POST /api/reservations
├── Crea reserva con transacción atómica
├── Asigna alumnos a la reserva
├── Si está pagada: crea JugadorGasto + deduce saldo del alumno
├── Commit + WAL checkpoint
└── Reserva visible en calendario y listado
```

**Flujo de eliminación de reserva:**
```
DELETE /api/reservations/{id}
├── Identifica alumnos que pagaron (paid=1)
├── Reembolsa saldo a cada alumno automáticamente
├── Elimina gastos asociados (JugadorGasto)
├── Elimina reserva (cascade elimina reservation_students)
├── Commit + WAL checkpoint
└── Saldos actualizados correctamente
```

**Logs de operación exitosa:**
```
DEBUG DELETE: reservation 104, expenses=1, paid_students={121}
DEBUG DELETE: Refunding 35.00€ to user 121
DEBUG DELETE: Transaction committed successfully for deleted reservation 104
DEBUG DELETE: WAL checkpoint forced for deleted reservation 104
```

**Características clave:**
- ✅ Transacciones atómicas (todo o nada)
- ✅ Reembolso automático de matrículas al eliminar reservas
- ✅ Sincronización WAL inmediata para persistencia garantizada
- ✅ Consistencia de datos entre reservas, gastos y saldos de usuarios
- ✅ Calendario muestra todas las reservas (parámetro `size=0`)

### Configuración SQLite (WAL Mode)
La base de datos está configurada para máxima durabilidad:
- `PRAGMA journal_mode=WAL` - Mejor concurrencia
- `PRAGMA synchronous=FULL` - Escrituras sincronizadas a disco
- `PRAGMA busy_timeout=30000` - 30 segundos timeout
- Pool: 5 conexiones base + 10 overflow
- `force_wal_checkpoint()` después de cada transacción crítica

### Health Check
Endpoint `/api/health` verifica:
- Conexión a base de datos
- Configuración SQLite (WAL mode, synchronous, etc.)
- Cantidad de reservas
- Tamaño de archivos de BD y WAL

## Notas Finales para Desarrollo
- **No usar `current_user()` como dependencia condicional asíncrona ciega**, requiere precaución ya que depende fuertemente de `request.session`.
- Si se añaden **nuevas entidades** a la Base de Datos, se inicializarán automáticamente gracias a `Base.metadata.create_all(bind=engine)` invocado en el ciclo de vida de `main.py` y `crear_usuarios.py`.
- **Atención a tipos**: `UserHealth` y `UserConsent` utilizan tipo `VARCHAR`/`String` para poder aceptar Strings puros como 'si', 'no'.
