# Model: ChatMessage

**Table**: `clinic_chat_messages` (new)

## Purpose

A written conversation between a doctor and a patient, independent of any specific booking — per
the brief, sending a chat inquiry is explicitly **not** the same as booking a consultation.
Broadcast in near-real-time via Laravel Reverb (confirmed dependency, see `00-overview.md`).

## Fields

| Column | Type | Constraints | Notes |
|---|---|---|---|
| `id` | bigint | PK | |
| `doctor_id` | bigint | `foreignId('doctor_id')->constrained('clinic_doctors')->cascadeOnDelete()` | the doctor side of the conversation |
| `patient_id` | bigint | `foreignId('patient_id')->constrained('users')->cascadeOnDelete()` | the patient side |
| `sender_type` | string | `'doctor'` / `'patient'` | which side sent this particular message |
| `body` | text | nullable if an attachment-only message | |
| `attachment_path` | string | nullable | images/files per the brief ("prescriptions, scans, reports") |
| `attachment_type` | string | nullable | mime-derived, for rendering (image vs. file) |
| `read_at` | datetime | nullable | |
| `created_at` / `updated_at` | timestamp | | |

A `(doctor_id, patient_id)` pair identifies "the conversation" — no separate `conversations`
table needed at this scale; index `(doctor_id, patient_id, created_at)` for fast thread loads.

## Relationships

| Relation | Type | Target |
|---|---|---|
| `doctor()` | `belongsTo` | `Doctor` |
| `patient()` | `belongsTo` | `User` |

## Real-time delivery

- `ChatService::send()` creates the row, then broadcasts a `NewClinicChatMessage` event over a
  private Reverb channel scoped to the `(doctor_id, patient_id)` pair.
- If the recipient isn't connected to the channel (offline), `ChatService` also calls
  `NotificationService` to create a database notification, following the existing
  `DatabaseNotification::create()` pattern rather than a new notification mechanism.

## Decisions

- File/image attachments stored the same way other file uploads in this app are stored (check
  the existing `ImageUploadTrait`/`ImageProcessing` traits used elsewhere — reuse rather than
  reinvent upload handling) rather than introducing a new upload pipeline.
