# 04 — مشخصات REST API

## 1. قواعد کلی

| مورد | قرارداد |
|---|---|
| Base URL | `/api/v1` |
| فرمت | JSON (UTF-8) |
| احراز هویت | `Authorization: Bearer <JWT>` (والد) یا `X-Child-Session: <token>` (کودک) |
| خطاها | `{ "error": { "code": "NOT_FOUND", "message": "...", "details": {} } }` |
| زمان | ISO-8601 UTC |
| Rate limit | 60 req/min به‌ازای IP (بازی آفلاین است؛ سرور فقط سینک/گزارش می‌گیرد) |

## 2. احراز هویت

### POST `/auth/parent/register` · `/auth/parent/login`
```json
// 200 OK
{ "token": "eyJhbGci...", "parent": { "id": "…", "email": "parent@example.com" } }
```

### POST `/children/:id/session` — لاگین آواتاری کودک
```jsonc
// Request (از روی دستگاه، بدون رمز؛ والد یک‌بار دستگاه را فعال کرده)
{ "avatar": "fox", "color": "green" }

// 200 OK
{ "childSession": "cst_9f2b…", "child": { "id": "…", "nickname": "آوا", "activePack": "fa" } }
```

> توکن کودک فقط به scope محدود کودک دسترسی دارد (`progress:*`, `events:write`) — اجازه‌ی هیچ عملیات مدیریتی ندارد.

## 3. پکیج‌های زبانی

| Method | مسیر | توضیح |
|---|---|---|
| GET | `/packs` | فهرست زبان‌ها: `[{packId, label, version, dir, coverageIcon}]` |
| GET | `/packs/:packId` | پک کامل — با `ETag`/`Cache-Control: max-age` برای کش مرورگر |

پک JSON استاتیک است؛ می‌تواند کاملاً از CDN سرو شود و این endpoint صرفاً fallback و کش آفلاین است.

## 4. پیشرفت (زیر scope کودک)

### GET `/children/:id/progress?pack=fa`
```jsonc
// 200 OK
{ "pack": "fa", "stars": 5, "badges": ["fa_master_5"], "letters": [
    { "letterId": "fa_be", "mastery": 3, "leitnerBox": 2, "dueAt": "2026-09-24T10:00:00Z",
      "correct": 12, "wrong": 1 },
    { "letterId": "fa_pe", "mastery": 1, "leitnerBox": 0, "dueAt": "2026-09-23T15:00:00Z",
      "correct": 3, "wrong": 4 }
] }
```

### POST `/children/:id/progress:batch` — همگام‌سازی دسته‌ای از SyncQueue
```jsonc
// Request — آرایه‌ای از تغییرات محلی + نشان‌ها/ستاره‌های کل (idempotent)
{ "pack": "fa",
  "changes": [
    { "letterId": "fa_be", "mastery": 3, "correct": 12, "wrong": 1, "leitnerBox": 2,
      "dueAt": "2026-09-26T10:00:00Z", "clientTs": "2026-09-23T09:58:11Z" },
    { "letterId": "fa_pe", "mastery": 1, "correct": 3,  "wrong": 4, "leitnerBox": 0,
      "dueAt": "2026-09-24T10:00:00Z", "clientTs": "2026-09-23T09:59:02Z" }
  ],
  "badges": ["fa_master_5"],       // اختیاری — upsert بدون تکرار
  "stars": 5                       // اختیاری — merge با max
}

// 200 OK — conflicts همیشه خالی است (ادغام نرم؛ رکوردی رد نمی‌شود)
{ "accepted": 2, "conflicts": [] }
```

**سیاست ادغام (نیم‌شبکه‌ای — idempotent و امن برای retry):**
- `mastery` / `correct` / `wrong` / `stars` → **max** (هر دو سمت فقط «به جلو» می‌روند)
- `leitnerBox` / `dueAt` → از رکورد با **تلاش بیشتر** (اطلاعات تازه‌تر)

### POST `/children/:id/events` — رویدادهای خام (batch تا ۱۰۰ مورد)
```jsonc
{ "events": [
    { "gameId": "letter-pop", "letterId": "fa_be", "verb": "success",
      "result": { "durationMs": 1840, "choiceCount": 4 }, "ts": "…" }
]}
```

## 5. گزارش والدین (scope والد)

### GET `/children/:id/report?pack=fa&days=7`
```jsonc
// 200 OK
{
  "summary": { "lettersIntroduced": 9, "lettersMastered": 4, "avgSessionMin": 11 },
  "weakLetters": ["fa_pe", "fa_se"],          // بر پایه‌ی نرخ خطا
  "dueToday": ["fa_be", "fa_lam"],
  "timeline": [ { "date": "2026-09-22", "sessions": 2, "stars": 7 } ]
}
```

## 6. مدیریت پروفایل

| Method | مسیر | scope | توضیح |
|---|---|---|---|
| POST | `/children` | والد | ساخت پروفایل کودک |
| GET | `/children` | والد | فهرست کودکان خانواده |
| PATCH | `/children/:id` | والد | تغییر نام مستعار/پک فعال/سقف زمانی |
| DELETE | `/children/:id` | والد | حذف کامل داده‌ها (حق فراموشی — GDPR) |
| GET | `/children/:id/badges` | کودک | نشان‌های کسب‌شده (برای اتاق جوایز) |

## 7. کدهای خطای استاندارد

| کد HTTP | code | سناریو |
|---|---|---|
| 400 | `VALIDATION_ERROR` | بدنه‌ی نامعتبر (جزئیات در `details`) |
| 401 | `UNAUTHORIZED` | توکن غایب/منقضی |
| 403 | `WRONG_SCOPE` | توکن کودک برای عملیات والدین |
| 404 | `NOT_FOUND` | کودک/پک/حروف وجود ندارد |
| 409 | `CONFLICT` | — (تعارض‌های سینک به‌صورت soft-merge حل می‌شوند) |
| 413 | `PAYLOAD_TOO_LARGE` | batch > 100 |
| 429 | `RATE_LIMITED` | عبور از سقف نرخ |

## 8. مثال جریان کامل سینک

```
1. (آفلاین) کودک بازی می‌کند → 12 تغییر در ap.queue
2. رویداد 'online' → SyncQueue.flush()
3. POST /children/:id/progress:batch   → 200 {accepted:12}
4. POST /children/:id/events           → 200
5. GET  /children/:id/badges           → { badges: [..., 'fa_master_5'] }  ← نشان جدید!
6. UI: ماسکات جشن می‌گیرد (GSAP 'star-reveal') حتی وسط جلسه
```
