# Milestone 5 — WhatsApp Automation (Implementation Notes)

The headline module. Every send is **persisted first** (conversation log),
then dispatched to the queue, so requests never block on the provider and
nothing is ever lost.

## Drivers (pluggable)
`config/whatsapp.php` → `log` (dev, writes to laravel.log), `meta` (WhatsApp
Cloud API), `twilio`. All implement `WhatsAppDriver` with `sendText` +
`sendDocument`. Provider responses are normalized to `SendResult`
(ok / id / failureCode / reason / retryable).

## Conversation log + delivery status (#1)
`wa_messages` stores body, status, provider id, failure code+reason, attempts,
timestamps, and a polymorphic `related` (appointment / recall / installment /
plan / invoice). Statuses: queued → sent → delivered → read, or failed /
retrying. Meta webhook (`/whatsapp/webhook`) applies delivery/read receipts.
Two views: clinic-wide log (filterable) + per-patient thread.

## One-click contact (#2)
Patient profile + thread: "Open in WhatsApp" (wa.me deep link) and in-app
"Message history" / free-text send. A WhatsApp tab sits in the patient tab bar.

## Failed-message retry queue (#3)
On a retryable failure (429/5xx/network) the message goes to `retrying` with
backoff (`retry_backoff_minutes` per attempt); a 5-minute scheduler
(`whatsapp:dispatch --retries` → `sweepRetries`) re-dispatches due messages.
After `max_attempts` it becomes `failed` with the reason recorded; owners can
hit Retry manually. Retry policy lives in the manager, not the queue (tries=1).

## Recall integration (#4)
`recalls` table (scheduled/sent/booked/dismissed/expired). "Set next visit" on
the patient profile (and `recall_months` on appointment completion) creates a
recall; `whatsapp:dispatch --recalls` (daily) sends the bilingual recall
template with the booking link and marks it sent. Recalls screen for triage.

## Custom clinic templates (#5)
`wa_templates` holds per-clinic ar/en overrides for 9 keys; defaults seeded at
signup and shown in the editor when no override exists. `TemplateRenderer`
resolves override → default, picks the clinic's locale, fills `{tokens}`, and
honors an `enabled` switch. Token help shown per template in the editor.

## Other automations
- Confirmation on reception booking + on pending→confirmed transition.
- 24h / 1h reminders (idempotent via `reminder_*_sent_at`), every 10 min.
- Installment due reminders (`installments.reminder_sent_at`), daily.
- Review gateway: completed visit → signed link → ≥4★ redirects to the
  clinic's Google URL, ≤3★ captured privately in `review_feedback`.

## PDF attachments (v2.1-C)
Plan / invoice PDFs (mPDF) are written to the private disk and sent as
`document` messages; the provider fetches them via a short-lived **signed**
URL (`/wa-media/{uuid}`, 30 min, signature = auth). Buttons on the plan and
invoice pages.

## Scheduler (register the Laravel cron)
```
whatsapp:dispatch --retries        every 5 min
whatsapp:dispatch --reminders      every 10 min
whatsapp:dispatch --installments   daily 10:00
whatsapp:dispatch --recalls        daily 10:30
whatsapp:dispatch --reviews        hourly
```
Run a queue worker (`php artisan queue:work`) — sends are queued jobs.

## Tests (19 new)
Engine: queue+dispatch, opt-out, deliver→sent, retry→fail-after-max, retry
sweep, webhook status, custom override, disabled template. Automation:
confirmation on booking, idempotent 24h reminder, recall send+mark, installment
reminder once, review request. Recall/review: 5★→Google redirect, low rating
captured, signature required, set-next-visit creates a recall.
