# تسک ۱۰: ماژول نوبت‌دهی

## توضیح
سیستم رزرو نوبت شامل نمایش اسلات‌های خالی، رزرو نوبت، لغو نوبت،
روزهای غیرقابل رزرو و لیست نوبت‌های کاربر.

## Endpoint ها

| متد | مسیر | توضیح | نیاز به Auth |
|-----|------|-------|-------------|
| GET | `/api/v1/appointment-slots` | اسلات‌های خالی دکتر در تاریخ | خیر |
| POST | `/api/v1/appointment` | رزرو نوبت | بله |
| GET | `/api/v1/appointment/not-available/{doctorId}` | روزهای غیرقابل رزرو | خیر |
| GET | `/api/v1/appointment/my-appointments/{userId}` | نوبت‌های من | بله |
| PATCH | `/api/v1/appointment/{uuid}/cancel` | لغو نوبت توسط کاربر | بله (Owner) |
| PATCH | `/api/v1/appointment/{uuid}/status` | تغییر وضعیت نوبت | بله (Doctor/Secretary/Admin) |

## پیش‌نیازها
- تسک ۰۱، ۰۲، ۰۵ (Doctor)، ۰۹ (تنظیمات)، ۱۵ (Payment)

## زمان تخمینی
۱۲ تا ۱۵ ساعت

---

## Status Machine نوبت

```
[ایجاد نوبت]
      ↓
waiting_for_payment ──→ (پرداخت موفق) ──→ reserved
      ↓                                        ↓
   (لغو)                          ┌────────────┤
      ↓                           │            │
  cancelled_by_patient      checked_in    (لغو دکتر)
                                  ↓            ↓
                               waiting   cancelled_by_doctor
                                  ↓
                            in_progress
                                  ↓
                    ┌─────────────┴─────────────┐
                    ↓                           ↓
                 visited                    no_show
                    ↓
                completed
```

**وضعیت‌ها:**
| وضعیت | توضیح | چه کسی تغییر می‌دهد |
|--------|-------|---------------------|
| `waiting_for_payment` | منتظر پرداخت | سیستم — بعد از رزرو |
| `reserved` | رزرو شده — پرداخت موفق | سیستم — بعد از تأیید پرداخت |
| `checked_in` | بیمار به مطب رسیده | منشی/دکتر |
| `waiting` | در صف انتظار مطب | منشی/دکتر |
| `in_progress` | ویزیت در حال انجام | منشی/دکتر |
| `visited` | ویزیت انجام شد | منشی/دکتر |
| `no_show` | بیمار نیامد | منشی/دکتر |
| `completed` | کامل شد | سیستم |
| `cancelled_by_patient` | لغو توسط بیمار | بیمار (Owner) |
| `cancelled_by_doctor` | لغو توسط دکتر | دکتر/Admin |
| `postponed` | به تعویق افتاده | دکتر/Admin |

---

## فلوی کامل رزرو + پرداخت

```
POST /api/v1/appointment
    ↓
1. بررسی اسلات: آیا time در آن date خالی است؟
2. بررسی holiday/date_override
3. ایجاد appointment با status=waiting_for_payment
4. بازگشت uuid نوبت به کلاینت
    ↓
POST /api/v1/payment (در task-15)
    { appointment_uuid: "...", payment_method: "mellat" }
    ↓
5. ایجاد payment با status=pending
6. دریافت payment_url از درگاه
7. redirect کاربر به درگاه
    ↓
[Callback از درگاه بانک]
    ↓
8. تأیید پرداخت → payments.status = 'received'
9. appointments.status = 'reserved'
10. واریز کمیسیون به کیف پول نماینده (اگر از دامنه نماینده)
```

**⚠ نکته:** اگر در ۳۰ دقیقه پرداخت نشود → `waiting_for_payment` به `cancelled_by_system` تغییر کند (job)

---

## GET /api/v1/appointment-slots

```
Query params:
  doctor_uuid (الزامی)
  date (الزامی) — فرمت: YYYY-MM-DD
```

```json
{
  "success": true,
  "data": {
    "date": "2024-03-20",
    "doctor": { "uuid": "...", "name": "دکتر احمدی" },
    "slots": [
      { "time": "09:00", "available": true, "duration": 30 },
      { "time": "09:30", "available": false, "duration": 30 },
      { "time": "10:00", "available": true, "duration": 30 }
    ]
  }
}
```

**منطق محاسبه اسلات‌های خالی:**
```
1. بارگذاری weekly_schedule دکتر برای روز هفته مربوطه
2. بررسی date_override برای تاریخ مشخص
3. بررسی holiday (اگر تاریخ در بازه تعطیلی است → همه اسلات‌ها unavailable)
4. خواندن نوبت‌های موجود با status ≠ cancelled → آن اسلات‌ها unavailable
5. بازگشت لیست اسلات‌ها با وضعیت available/unavailable
```

---

## POST /api/v1/appointment

```json
// Request
{
  "doctor_uuid": "61be915b-...",
  "date": "2024-03-20",
  "time": "09:00",
  "address_id": 39,
  "insurance_type_id": null,
  "notes": "درد معده دارم"
}

// Response 201
{
  "success": true,
  "data": {
    "uuid": "...",
    "doctor": { "uuid": "...", "name": "دکتر احمدی" },
    "date": "2024-03-20",
    "time": "09:00",
    "status": "waiting_for_payment",
    "created_at": 1748000000
  }
}

// Response 409 — اسلات گرفته شده
{
  "success": false,
  "errors": [{ "code": "ERR_APPOINTMENT_001", "message": "اسلات انتخاب‌شده در دسترس نیست" }]
}
```

---

## PATCH /api/v1/appointment/{uuid}/cancel — لغو نوبت

```json
// Request
{ "reason": "به دلیل بیماری نمی‌توانم بیایم" }

// Response 200
{
  "success": true,
  "data": {
    "uuid": "...",
    "status": "cancelled_by_patient",
    "refund_status": "pending"
  }
}

// Response 400 — نوبت قابل لغو نیست
{
  "success": false,
  "errors": [{ "code": "ERR_APPOINTMENT_002", "message": "نوبت در وضعیت فعلی قابل لغو نیست" }]
}
```

**قوانین لغو:**
- فقط نوبت‌های با status `waiting_for_payment` یا `reserved` قابل لغو هستند
- اگر پرداخت شده (`reserved`) → `payments.status = 'refund'` و refund شروع می‌شود
- لغو بعد از `checked_in` فقط توسط Admin/Doctor مجاز است

---

## PATCH /api/v1/appointment/{uuid}/status

```json
// Request (Doctor/Secretary/Admin)
{ "status": "checked_in" }

// Response 200
{
  "success": true,
  "data": {
    "uuid": "...",
    "status": "checked_in",
    "updated_at": 1748000000
  }
}
```

**Transition های مجاز:**
```
reserved        → checked_in      (Doctor/Secretary)
checked_in      → waiting         (Doctor/Secretary)
waiting         → in_progress     (Doctor/Secretary)
in_progress     → visited         (Doctor/Secretary)
in_progress     → no_show         (Doctor/Secretary)
visited         → completed       (System/Doctor)
reserved        → cancelled_by_doctor (Doctor/Admin)
reserved        → postponed       (Doctor/Admin)
```

---

## GET /api/v1/appointment/not-available/{doctorId}

```json
{
  "success": true,
  "data": {
    "not_available_dates": [
      "2024-03-20",
      "2024-03-21",
      "2024-04-01"
    ]
  }
}
```

**منطق:**
- روزهایی که holiday هستند
- روزهایی که date_override با `active=false` تعریف شده
- روزهایی که همه اسلات‌ها پر هستند

---

## GET /api/v1/appointment/my-appointments/{userId}

```
Query params:
  status (اختیاری) — فیلتر بر اساس وضعیت
  page (اختیاری، پیش‌فرض 1)
  limit (اختیاری، پیش‌فرض 10)
```

```json
{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "doctor": {
        "uuid": "...",
        "name": "دکتر احمدی",
        "specialty": "قلب و عروق",
        "img": [{ "url": "..." }]
      },
      "date": "2024-03-20",
      "time": "09:00",
      "status": "reserved",
      "payment_status": "received",
      "created_at": 1748000000
    }
  ],
  "meta": { "totalRecords": 12, "totalPages": 2, "currentPage": 1 }
}
```

---

## نکات مهم

- **Optimistic Locking:** هنگام رزرو اسلات، از Transaction + Lock استفاده شود تا race condition نباشد
- **Expiry Job:** نوبت‌های `waiting_for_payment` بعد از ۳۰ دقیقه باید auto-cancel شوند (Symfony Scheduler)
- **N+1 Prevention:** در لیست نوبت‌ها، دکتر و وضعیت پرداخت با eager loading بارگذاری شوند
- **Timestamps:** همه تاریخ/زمان‌ها Unix timestamp (INT) ذخیره می‌شوند
