# Flujo — Creación de Partida (Privada / Pública)

## Descripción general

El flujo de creación de una partida consta de 6 llamadas secuenciales.
El socio selecciona su deporte y tipo de partida, elige un grupo, verifica disponibilidad,
selecciona fecha y cancha, revisa el resumen y finalmente confirma la reserva.

```
[1] Iniciar reserva       →  elige deporte + tipo           →  lista de grupos
[2] Integrantes del grupo →  elige grupo                    →  jugadores + disponibilidad
[3] Validar grupo         →  confirma grupo                 →  calendario 14 días
[4] Canchas disponibles   →  elige fecha del calendario     →  horarios + canchas
[5] Resumen de partida    →  elige cancha y horario         →  resumen antes de confirmar
[6] Guardar reserva       →  socio confirma                 →  reserva creada + invitaciones
```

> Todos los endpoints requieren `Authorization: Bearer {token}` (Sanctum).
> Los IDs (`idGrupo`, `idDeporte`, `idCancha`, `idHorario`) siempre viajan en **Base64**.

---

## Llamada 1 — Iniciar reserva

**`POST /api/v1/deportescam/reservar/iniciar`**

Recibe el deporte y el tipo de partida. Retorna los grupos del socio autenticado
para ese deporte donde tiene membresía activa y su invitación fue aprobada.

### Body

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `idDeporte` | string (Base64) | ✅ | ID del deporte |
| `tipoPardida` | string | ✅ | `Publica` o `Privada` |

### Respuesta exitosa `200`

```json
{
  "success": true,
  "message": "Grupos obtenidos correctamente.",
  "data": [
    {
      "idGrupo": "MQ==",
      "grupoNo": "2024000001",
      "grupoAlias": "Los Ases",
      "totalActivos": 4
    }
  ]
}
```

### Respuesta sin grupos `422`

```json
{
  "success": false,
  "message": "No tienes grupos activos para el deporte seleccionado."
}
```

---

## Llamada 2 — Integrantes del grupo

**`GET /api/v1/deportescam/grupo/integrantes?idGrupo=MQ==`**

Recibe el grupo seleccionado en la Llamada 1 y retorna la lista de integrantes
activos con su estado de registro, estado de invitación y disponibilidad para jugar.

> Solo accesible para miembros del grupo con invitación `Aprobada`.

### Query params

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `idGrupo` | string (Base64) | ✅ | ID del grupo |

### Respuesta exitosa `200`

```json
{
  "success": true,
  "message": "Integrantes obtenidos correctamente.",
  "data": {
    "idGrupo": "MQ==",
    "grupoNo": "2024000001",
    "grupoAlias": "Los Ases",
    "totalIntegrantes": 4,
    "integrantes": [
      {
        "nombre": "JUAN PÉREZ GARCÍA",
        "membresiaNo": "12345",
        "noJugador": 3,
        "estadoRegistro": "Activo",
        "estadoInvitacion": "Aprobada",
        "disponible": true
      },
      {
        "nombre": "CARLOS RUIZ LOPEZ",
        "membresiaNo": "67890",
        "noJugador": 7,
        "estadoRegistro": "Activo",
        "estadoInvitacion": "Aprobada",
        "disponible": false
      }
    ]
  }
}
```

#### Campo `disponible`

| Valor | Significado |
|---|---|
| `true` | El jugador puede participar en la nueva partida |
| `false` | Ya tiene 2 reservas activas (`Temporal` o `Reservada`) |

#### Campo `estadoRegistro`

Estado de la cuenta del jugador en `pad_registropadel`. Valores posibles: `Activo`, `Bloqueado`, `Inactivo`.

#### Campo `estadoInvitacion`

Estado en el grupo. Solo se incluyen jugadores con `grupoJugadorEstado = Activo`.
Valores posibles: `Aprobada`, `Solicitada`, `Rechazada`.

### Error `422` — grupo no existe o inactivo

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": { "idGrupo": ["El grupo no existe o no está activo."] }
}
```

### Error `422` — sin acceso al grupo

```json
{
  "success": false,
  "message": "No tienes acceso a este grupo."
}
```

---

## Llamada 3 — Validar grupo y obtener calendario

**`POST /api/v1/deportescam/reservar/validar-grupo`**

Valida que el grupo tenga al menos 4 jugadores disponibles y retorna el calendario
de los próximos 14 días indicando en qué días se puede reservar cancha.

> Un jugador no cuenta si ya tiene 2 reservas activas (`Temporal` o `Reservada`).

### Body

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `idGrupo` | string (Base64) | ✅ | ID del grupo confirmado en la Llamada 2 |

### Respuesta exitosa `200`

```json
{
  "success": true,
  "message": "Grupo validado correctamente.",
  "data": {
    "calendario": [
      { "fecha": "2026-06-10", "dia": "Miércoles", "estado": "Disponible" },
      { "fecha": "2026-06-11", "dia": "Jueves",    "estado": "Día no hábil" },
      { "fecha": "2026-06-12", "dia": "Viernes",   "estado": "No disponible" }
    ]
  }
}
```

#### Estados del calendario

| Estado | Significado |
|---|---|
| `Disponible` | Al menos un horario-cancha libre para ese deporte ese día |
| `Día no hábil` | Fecha marcada como inhábil en el sistema |
| `No disponible` | No hay horarios configurados ese día, o todos los slots están ocupados |

### Error `422` — sin acceso al grupo

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": { "idGrupo": ["No tienes acceso a este grupo."] }
}
```

### Error `422` — jugadores insuficientes

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": { "grupo": ["El grupo no tiene suficientes jugadores disponibles. Se necesitan al menos 4 (actualmente: 2)."] }
}
```

---

## Llamada 4 — Canchas disponibles por fecha

**`GET /api/v1/deportescam/cancha/disponibles?idDeporte=MQ==&fecha=2026-06-15`**

Recibe el deporte y la fecha elegida del calendario (Llamada 3). Retorna todos los horarios
activos del deporte para ese día con las canchas disponibles. Si la fecha es hoy,
solo se incluyen horarios cuya hora de inicio aún no ha pasado.

### Query params

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `idDeporte` | string (Base64) | ✅ | ID del deporte |
| `fecha` | string (`YYYY-MM-DD`) | ✅ | Fecha elegida del calendario, hoy o futura |

### Respuesta exitosa `200`

```json
{
  "success": true,
  "message": "Canchas disponibles obtenidas correctamente.",
  "data": {
    "horarios": [
      {
        "idHorario": "MQ==",
        "horarioInicio": "08:00",
        "horarioFin": "09:30",
        "totalDisponibles": 2,
        "totalOcupadas": 1,
        "canchas": [
          { "idCancha": "MQ==", "canchaNo": 1, "ubicacion": "Cuautla" },
          { "idCancha": "Mg==", "canchaNo": 2, "ubicacion": "Cuautla" }
        ]
      },
      {
        "idHorario": "Mg==",
        "horarioInicio": "10:00",
        "horarioFin": "11:30",
        "totalDisponibles": 0,
        "totalOcupadas": 3,
        "canchas": []
      }
    ]
  }
}
```

> Cuando `totalDisponibles = 0` y `canchas = []`, ese horario está completamente ocupado.
> El front puede mostrarlo deshabilitado para que el socio vea por qué no puede elegirlo.

### Respuesta `200` — sin disponibilidad

```json
{
  "success": true,
  "message": "No hay horarios disponibles para esta fecha.",
  "data": { "horarios": [] }
}
```

### Error `422`

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": {
    "fecha": ["El formato de fecha debe ser YYYY-MM-DD."]
  }
}
```

> Los horarios se devuelven ordenados por `horarioInicio` ascendente.

---

## Llamada 5 — Resumen de partida

**`POST /api/v1/deportescam/reservar/resumen`**

Recibe la selección completa del socio (tipo, grupo, cancha, horario y fecha).
Verifica que el slot siga disponible y retorna el resumen formateado para
que el socio confirme antes de guardar.

### Body

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `tipo` | string | ✅ | `Privada` o `Publica` |
| `idGrupo` | string (Base64) | ✅ | ID del grupo |
| `idCancha` | string (Base64) | ✅ | ID de la cancha elegida en Llamada 4 |
| `idHorario` | string (Base64) | ✅ | ID del horario elegido en Llamada 4 |
| `fecha` | string (`YYYY-MM-DD`) | ✅ | Fecha elegida en Llamada 3 |

### Respuesta exitosa `200`

```json
{
  "success": true,
  "message": "Resumen obtenido correctamente.",
  "data": {
    "partida": "Privada",
    "tipo": "Con grupos — Los Ases",
    "cancha": "Cancha 1",
    "horario": "08:00 - 09:30",
    "organizador": "JUAN PÉREZ GARCÍA"
  }
}
```

#### Campo `tipo` según `partida`

| `partida` | `tipo` |
|---|---|
| `Privada` | `Con grupos — {alias del grupo}` |
| `Publica` | `Pública` |

### Error `422` — cancha ocupada

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": { "cancha": ["La cancha ya no está disponible para el horario y fecha seleccionados."] }
}
```

### Error `422` — validación de campos

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": {
    "tipo":      ["El tipo de partida debe ser Privada o Publica."],
    "idGrupo":   ["El grupo no existe o no está activo."],
    "idCancha":  ["La cancha no existe o no está activa."],
    "idHorario": ["El horario no existe o no está activo."],
    "fecha":     ["La fecha debe ser hoy o una fecha futura."]
  }
}
```

---

## Llamada 6 — Guardar reserva

**`POST /api/v1/deportescam/reservar/guardar`**

Confirma la reserva. Vuelve a verificar que el slot siga disponible y que el grupo
aún tenga al menos 4 jugadores elegibles. Crea la reserva, registra la partida,
agrega a cada jugador disponible y envía invitaciones por correo (y WhatsApp si lo tiene activo).

> Solo se registran jugadores con `grupoJugadorEstado = Activo`, `grupoJugadorEstadoInvitacion = Aprobada`,
> `registroPadelEstado = Activo` y con menos de 2 reservas activas.
> El organizador queda registrado como jugador pero no recibe invitación.

### Body

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `tipo` | string | ✅ | `Privada` o `Publica` |
| `idGrupo` | string (Base64) | ✅ | ID del grupo |
| `idCancha` | string (Base64) | ✅ | ID de la cancha |
| `idHorario` | string (Base64) | ✅ | ID del horario |
| `fecha` | string (`YYYY-MM-DD`) | ✅ | Fecha de la partida |

### Respuesta exitosa `200`

```json
{
  "success": true,
  "message": "Partida creada correctamente.",
  "data": {
    "idPartida": "MQ==",
    "idReserva": "MQ=="
  }
}
```

### Error `422` — cancha ocupada (re-validación final)

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": { "cancha": ["La cancha ya no está disponible para el horario y fecha seleccionados."] }
}
```

### Error `422` — jugadores insuficientes (re-validación final)

```json
{
  "success": false,
  "status": 422,
  "message": "Tienes errores de validación",
  "errors": { "grupo": ["El grupo ya no tiene suficientes jugadores disponibles. Se necesitan al menos 4 (actualmente: 2)."] }
}
```

### Error `422` — slot ocupado al momento de guardar (race condition detectada)

```json
{
  "success": false,
  "message": "La cancha ya no está disponible para el horario y fecha seleccionados."
}
```

> Esta respuesta ocurre si dos socios enviaron el guardar al mismo tiempo y el otro llegó primero.

---

## Notas generales

- Todos los endpoints requieren `Authorization: Bearer {token}`.
- Los IDs (`idGrupo`, `idDeporte`, `idCancha`, `idHorario`) siempre viajan en **Base64**.
- El middleware `socioActivo` verifica en cada llamada que el socio sigue `Activo`; si fue bloqueado su sesión se revoca automáticamente con `401`.
- Un jugador no es elegible si ya tiene 2 o más reservas en estado `Temporal` o `Reservada`.
- La reserva se crea con estado `Temporal`. Cambia a `Reservada` cuando el jugador confirma su lugar.
- `ubicacion` siempre será `Cuautla` en todas las canchas.
