# Crazy Time — API administrativa y liquidaciones

Base URL: la del servidor de juego (ej. `https://tudominio.com`).

Autenticación: header `Authorization: Bearer admin123` (misma clave que el panel `/admin.html`).

---

## 1. Consultar ronda completa (resultado + apuestas registradas + pagos)

Útil para conciliar con la central de pagos.

```http
GET /api/admin/rounds/:gameRoundId
Authorization: Bearer admin123
```

**Parámetros de ruta**

- `gameRoundId` (número): identificador de la ronda de juego emitido en `round-start` / `new-round`.

**Respuesta 200**

```json
{
  "gameRoundId": 42,
  "round": {
    "segmentIndex": 3,
    "segmentLabel": "5",
    "segmentType": "number",
    "topSlot": { "multiplier": 5, "target": "5" },
    "bonusType": null,
    "bonusResult": null,
    "playersOnline": 12,
    "timestamp": "2026-04-02T12:00:00.000Z"
  },
  "betsSnapshot": {
    "socketId1": { "user": "D26512", "bets": { "1": 10, "5": 0 }, "totalBet": 10 }
  },
  "payouts": [
    {
      "_id": "...",
      "gameRoundId": 42,
      "user": "D26512",
      "operation": "debit",
      "amount": 50,
      "description": "Bet $50.00 - (CrazyTime) - Round: 42",
      "category": "bet",
      "webhookResponse": { "status": "success" },
      "success": true,
      "createdAt": "...",
      "updatedAt": "..."
    }
  ]
}
```

**Errores**

- `403` — token inválido.
- `404` — no existe documento de ronda para ese `gameRoundId`.

---

## 2. Reintentar todos los créditos fallidos de una ronda

Vuelve a llamar al webhook de crédito por cada registro `operation: credit` con `success: false` de esa ronda.

```http
POST /api/admin/payouts/retry-round
Authorization: Bearer admin123
Content-Type: application/json

{ "gameRoundId": 42 }
```

**Respuesta 200**

```json
{
  "gameRoundId": 42,
  "retried": 3,
  "results": [
    { "payoutId": "...", "user": "D26512", "amount": 120, "success": true, "webhookResponse": { "status": "success" } }
  ]
}
```

---

## 3. Reintentar un pago individual (por ID de ledger)

```http
POST /api/admin/payouts/retry-one
Authorization: Bearer admin123
Content-Type: application/json

{ "payoutId": "674a1b2c3d4e5f6789012345" }
```

**Respuesta 200**

```json
{
  "payoutId": "674a1b2c3d4e5f6789012345",
  "success": true,
  "webhookResponse": { "status": "success" }
}
```

Solo aplica a documentos del ledger (típicamente `credit`). El reintento actualiza `webhookResponse`, `success` y `updatedAt`.

**Errores**

- `404` — `payoutId` no existe.
- `400` — ID inválido.

---

## 4. Listar pagos recientes (opcional para integraciones)

```http
GET /api/admin/payouts?limit=50&gameRoundId=42&success=false
Authorization: Bearer admin123
```

Query opcionales:

- `limit` (default 100, máx 500)
- `gameRoundId` — filtrar por ronda
- `success` — `true` / `false`

---

## 5. Forzar resultado del próximo giro

Misma autenticación que el resto: **`Authorization: Bearer admin123`** (obligatorio; sin esto responde `403` con `{ "error": "No autorizado" }`).

**Forzar un segmento** (se aplica en la siguiente ronda cuando cierren apuestas):

```http
POST /api/admin/force
Authorization: Bearer admin123
Content-Type: application/json

{ "segment": "PA" }
```

Valores típicos de `segment`: `1`, `2`, `5`, `10`, `CF`, `PA`, `CH`, `CT` (deben existir en la ruleta del servidor). Respuesta 200: `{ "ok": true, "forced": "PA" }`.

**Forzar Top Slot** (objetivo + multiplicador que verán los jugadores antes del giro; se consume en la siguiente generación de ronda):

```http
POST /api/admin/force-ts
Authorization: Bearer admin123
Content-Type: application/json

{ "target": "5", "multiplier": 10 }
```

- `target`: uno de `1`, `2`, `5`, `10`, `CF`, `PA`, `CH`, `CT`.
- `multiplier`: uno de `2`, `3`, `5`, `7`, `10`, `15`, `20`, u omite / `null` / cadena vacía para **miss** (sin multiplicador en el slot).

Quitar solo el Top Slot forzado:

```http
POST /api/admin/force-ts
Authorization: Bearer admin123
Content-Type: application/json

{ "clear": true }
```

Respuestas: éxito `{ "ok": true, "topSlot": { "target": "5", "multiplier": 10 } }` o `{ "ok": true, "topSlot": null }` si se limpió. El servidor emite por Socket.IO `admin-force-ts` con `{ "topSlot": { ... } | null }` para sincronizar paneles.

**Quitar el forzado (ruleta y Top Slot):**

```http
POST /api/admin/force-clear
Authorization: Bearer admin123
```

**Consultar si hay forzado:**

```http
GET /api/admin/force-status
Authorization: Bearer admin123
```

Incluye `forced` (segmento de ruleta o `null`), `forcedTopSlot` (`{ target, multiplier }` o `null`), más `topSlotTargets`, `topSlotMultipliers` y `options` de segmentos.

En **Postman**: pestaña **Authorization** → tipo **Bearer Token** → token `admin123`, o en **Headers** añadir `Authorization` = `Bearer admin123` (con espacio después de Bearer).

---

## 6. Criterio de éxito del webhook

Se considera **éxito** cuando la respuesta JSON cumple **cualquiera** de estas condiciones:

- `status === true` (booleano), respuesta típica del webhook PHP:

```json
{ "status": true, "message": "Transaction processed successfully" }
```

- O bien `status` igual a la cadena `success` (sin distinguir mayúsculas).

En caso de error, PHP puede devolver `status: false` y un `message` descriptivo; ese texto se puede mostrar al jugador. Cualquier otra forma o error de red se guarda en `webhookResponse` con `success: false` para reintentos.

---

## 7. Sesión pública del jugador (sin token)

```http
GET /api/session?user=D26512
```

Sin `user` o con usuario inválido / sin saldo / sin permiso: respuesta **403** JSON `{ "error": "...", "canPlay": false }`. No hay modo invitado.

Permiso **Crazy Time**: el backend exige que en `userInfoExt` venga `crazy_time: true` (también acepta alias `crazytime` / `crazyTime` con valor verdadero). Si no, `403` con `reason: "crazy_time"`.

Si el usuario tiene **veto de juego** (moderación), `403` con `reason: "play_banned"` y `playBanUntil` (ISO).

En **éxito** (`canPlay: true`) puede venir `chatMutedUntil` (ISO o ausente): el cliente deshabilita el envío de chat hasta esa hora.

Socket.IO: `player-sanction` con `{ usernameNorm, muteUntil, playBanUntil }` para sincronizar al vuelo; `chat-error` con `code: "muted"` si intentan escribir silenciados.

## 8. Historial de rondas del jugador (sin token admin)

```http
GET /api/player/round-history?user=D26512
```

Devuelve rondas en las que el usuario tuvo un débito de apuesta registrado, con `roundCode` en formato 5 dígitos, resultado, Top Slot y bonus si aplica.

---

## 8b. Chat y moderación (admin, Bearer)

| Método | Ruta | Cuerpo | Descripción |
|--------|------|--------|-------------|
| GET | `/api/admin/chat?limit=120` | — | Historial reciente (incluye `role`: `user` \| `mod`). |
| POST | `/api/admin/chat/send-mod` | `{ "message": "..." }` | Publica mensaje como moderador; emite `chat-broadcast` a todos. |
| GET | `/api/admin/moderation/active` | — | Sanciones con `muteUntil` o `playBanUntil` en el futuro. |
| POST | `/api/admin/moderation/mute` | `{ "user": "D26512", "minutes": 15 }` | Extiende silencio de chat (min 1, máx 7 días). |
| POST | `/api/admin/moderation/unmute` | `{ "user": "..." }` | Quita silencio. |
| POST | `/api/admin/moderation/play-ban` | `{ "user": "...", "minutes": 60 }` | Impide apostar en CT (sesión + `place-bets`; máx ~30 días). |
| POST | `/api/admin/moderation/play-unban` | `{ "user": "..." }` | Quita veto de juego. |

El usuario se normaliza internamente en mayúsculas para coincidir con `?user=`.

---

## 9. Estadísticas y reportes (admin)

Todas usan `Authorization: Bearer …` y el mismo rango de fechas:

- `?days=7` (1–366), o bien `?from=YYYY-MM-DD&to=YYYY-MM-DD`.

| Método | Ruta | Descripción |
|--------|------|-------------|
| GET | `/api/admin/stats/overview` | KPIs: rondas, débitos, créditos OK/fallo, jugadores únicos, margen, desglose de bonos. |
| GET | `/api/admin/stats/top-credits?limit=25` | Mayores premios (créditos exitosos). |
| GET | `/api/admin/stats/segments` | Frecuencia por segmento de ruleta (%). |
| GET | `/api/admin/stats/leaderboard?limit=30` | Rankings `byWagered` y `byWon`. |
| GET | `/api/admin/players/report?user=D26512` | Resumen + movimientos del jugador en el período. |
| GET | `/api/admin/rounds-recent?limit=50` | Lista compacta de últimas rondas. |

`GET /api/admin/payouts` admite además `user=`, y filtro por fechas salvo cuando se pasa `gameRoundId` (entonces se listan todos los movimientos de esa ronda).

`GET /api/admin/live` — fase, cuenta atrás, jugadores conectados, pausa, `forcedSegment`, `forcedTopSlot` (misma forma que en `force-status`).

---

## Notas operativas

- Los **débitos** de apuesta también se registran en el mismo ledger (`operation: debit`, `category: bet`).
- El snapshot de apuestas por socket en memoria se adjunta al detalle de ronda mientras exista en servidor; tras reinicio puede faltar `betsSnapshot` parcialmente.
- Cambiar `admin123` en producción: constante `ADMIN_PASSWORD` en `server.js` y la misma clave en el panel.
