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

## وضعیت‌های واقعی نوبت (از Drupal)
```
waiting_for_payment   → وضعیت پیش‌فرض هنگام ثبت نوبت
confirmed             → بعد از پرداخت موفق
auto_cancel_unpaid    → لغو خودکار به دلیل عدم پرداخت
cancelled_by_patient  → لغو توسط بیمار
cancelled_by_doctor   → لغو توسط دکتر
```
⚠️ در طراحی اولیه `pending/cancelled/completed` بود — این‌ها **اشتباه** بودند.

## تشخیص نماینده از HTTP Host (Multi-tenant)
```php
// در AppointmentService.php Drupal:
// نماینده از domain_name=host پیدا می‌شود
private function getRepresentation(string $host): ?int {
    return $this->representationRepo->findByDomainName($host)?->getId();
}
// در Symfony: از $request->getHost() استفاده کن
$host = $request->getSchemeAndHttpHost() . '/';  // e.g. http://yasuj-nobat.localhost:3000/
$representation = $this->representationRepo->findByDomainName($host);
```

## فیلدهای واقعی نوبت (از کد Drupal)
```
field_doctor_id      → entity reference به doctor
field_start_time     → Unix timestamp (Asia/Tehran)
field_end_time       → Unix timestamp (Asia/Tehran)
field_address        → entity reference به doctor_address
field_slot           → JSON: {start_time_timestamp, end_time_timestamp, location_id, start, end, duration}
field_representation → entity reference به representation
field_status         → string (waiting_for_payment, confirmed, ...)
field_visited_at     → Unix timestamp (بعد از ویزیت)
field_info           → یادداشت
```

## اعتبارسنجی slot (از کد Drupal)
```php
// بررسی start_time معتبر بودن (در آینده، نه گذشته)
$checkStartTime = $this->isTimestampValid($startTime, 10); // 10 دقیقه حداقل
$checkEndTime   = $this->isTimestampValid($endTime, 10);

// بررسی تداخل (conflict check)
$unacceptableStatus = ['auto_cancel_unpaid', 'cancelled_by_patient', 'cancelled_by_doctor'];
// اگر نوبتی برای همین doctor + slot وجود داشت که status آن در لیست بالا نبود → خطا
```

## جلوگیری از Race Condition
از database transaction + pessimistic write lock استفاده کن:
```php
$this->entityManager->beginTransaction();
try {
    $existing = $this->repo->findConflictingAppointment(
        $doctorId, $startTime, $endTime,
        lockMode: LockMode::PESSIMISTIC_WRITE
    );
    if ($existing) throw new SlotAlreadyTakenException();

    $appointment = new Appointment(...);
    $this->entityManager->persist($appointment);
    $this->entityManager->flush();
    $this->entityManager->commit();
} catch (\Exception $e) {
    $this->entityManager->rollback();
    throw $e;
}
```

## Response کامل نوبت (از finalizedData Drupal)
```json
{
  "id": 1,
  "uuid": "...",
  "status": "waiting_for_payment",
  "start_time": 1716000000,
  "end_time":   1716001800,
  "visited_at": null,
  "info": null,
  "slot": {
    "start_time_timestamp": 1716000000,
    "end_time_timestamp":   1716001800,
    "location_id": 42,
    "start": "09:00",
    "end":   "09:30",
    "duration": 30
  },
  "doctor": {
    "id": 5,
    "uuid": "...",
    "name": "دکتر محمدی",
    "specialty": {"id": 3, "uuid": "...", "name": "متخصص قلب"}
  },
  "address": {
    "id": 42, "uuid": "...", "name": "مطب شیراز",
    "address": "...", "phone": "071...",
    "map": {"latitude": 29.6, "longitude": 52.5}
  },
  "patient": {
    "id": 10, "uuid": "...",
    "name": "علی رضایی",
    "mobile": "09120000000",
    "profile": {"id": 8, "uuid": "..."}
  }
}
```

## روزهای غیر قابل رزرو
endpoint `GET /appointment/not-available/{doctorId}` تاریخ‌هایی را برمی‌گرداند که در آن‌ها نوبت خالی نیست:
- روزهایی در Holiday جای گرفته‌اند
- روزهایی که DateOverride با active=false دارند
- روزهایی که WeeklySchedule آن‌ها active=false است
- روزهایی که همه slot‌هایشان رزرو فعال دارند

## مجوزها
```
POST /appointment                     → احراز هویت‌شده
GET /appointment-slots/{doctorId}     → عمومی
GET /appointment/not-available/{id}  → عمومی
GET /appointment/my-appointments/{id} → owner یا ROLE_ADMIN
PATCH /appointment/{uuid}/status     → ROLE_ADMIN یا دکتر مرتبط
```
