# Story 8.7: Consultation Reminder (2 Hours)

## Epic Reference
**Epic 8:** Email Notification System

## Dependencies
- **Story 8.1:** Email infrastructure setup (base template, queue config, SMTP)
- **Story 8.4:** BookingApprovedEmail pattern and CalendarService integration
- **Story 8.6:** Migration that adds `reminder_2h_sent_at` column to consultations table
- **Consultation Model:** Must have `status`, `scheduled_date`, `scheduled_time`, `consultation_type`, `payment_status` fields
- **User Model:** Must have `preferred_language` field

## User Story
As a **client**,
I want **to receive a reminder 2 hours before my consultation**,
So that **I'm prepared and ready for my appointment with final details and contact information**.

## Acceptance Criteria

### Trigger
- [ ] Scheduled artisan command runs every 15 minutes
- [ ] Find consultations approximately 2 hours away (within 7-minute window)
- [ ] Only for approved consultations (`status = 'approved'`)
- [ ] Skip cancelled/no-show/completed consultations
- [ ] Track sent reminders to prevent duplicates via `reminder_2h_sent_at`

### Content
- [ ] Subject: "Your consultation is in 2 hours" / "استشارتك بعد ساعتين"
- [ ] Consultation date and time (formatted per locale)
- [ ] Final payment reminder: Show if `consultation_type = 'paid'` AND `payment_status != 'received'`
- [ ] Office contact information for last-minute issues/questions

### Language
- [ ] Email rendered in client's `preferred_language` (ar/en)
- [ ] Date/time formatted according to locale

## Technical Notes

### Artisan Command
```php
// app/Console/Commands/Send2HourReminders.php
namespace App\Console\Commands;

use App\Models\Consultation;
use App\Notifications\ConsultationReminder2h;
use Carbon\Carbon;
use Illuminate\Console\Command;

class Send2HourReminders extends Command
{
    protected $signature = 'reminders:send-2h';
    protected $description = 'Send 2-hour consultation reminders';

    public function handle(): int
    {
        $targetTime = now()->addHours(2);
        $windowStart = $targetTime->copy()->subMinutes(7);
        $windowEnd = $targetTime->copy()->addMinutes(7);

        $consultations = Consultation::query()
            ->where('status', 'approved')
            ->whereNull('reminder_2h_sent_at')
            ->whereDate('scheduled_date', today())
            ->get()
            ->filter(function ($consultation) use ($windowStart, $windowEnd) {
                $consultationDateTime = Carbon::parse(
                    $consultation->scheduled_date->format('Y-m-d') . ' ' . $consultation->scheduled_time
                );
                return $consultationDateTime->between($windowStart, $windowEnd);
            });

        $count = 0;
        foreach ($consultations as $consultation) {
            $consultation->user->notify(new ConsultationReminder2h($consultation));
            $consultation->update(['reminder_2h_sent_at' => now()]);
            $count++;
        }

        $this->info("Sent {$count} 2-hour reminder(s).");

        return Command::SUCCESS;
    }
}
```

### Notification Class
```php
// app/Notifications/ConsultationReminder2h.php
namespace App\Notifications;

use App\Models\Consultation;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class ConsultationReminder2h extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public Consultation $consultation
    ) {}

    public function via(object $notifiable): array
    {
        return ['mail'];
    }

    public function toMail(object $notifiable): MailMessage
    {
        $locale = $notifiable->preferred_language ?? 'ar';
        $consultation = $this->consultation;

        $subject = $locale === 'ar'
            ? 'استشارتك بعد ساعتين'
            : 'Your consultation is in 2 hours';

        return (new MailMessage)
            ->subject($subject)
            ->markdown("emails.reminders.consultation-2h.{$locale}", [
                'consultation' => $consultation,
                'user' => $notifiable,
                'showPaymentReminder' => $this->shouldShowPaymentReminder(),
            ]);
    }

    private function shouldShowPaymentReminder(): bool
    {
        return $this->consultation->consultation_type === 'paid'
            && $this->consultation->payment_status !== 'received';
    }
}
```

### Email Templates

**Arabic Template:** `resources/views/emails/reminders/consultation-2h/ar.blade.php`
**English Template:** `resources/views/emails/reminders/consultation-2h/en.blade.php`

Template content should include:
- Greeting with client name
- Urgent reminder message ("Your consultation is in 2 hours")
- Date/time (formatted: `scheduled_date->translatedFormat()`)
- **Final payment reminder section** (conditional) - more urgent tone than 24h reminder
- Office contact information for last-minute issues (phone, email)
- Branded footer (from Story 8.1 base template)

**Note:** Unlike the 24-hour reminder, this template does NOT include a calendar download link (client should already have it from approval email and 24h reminder).

### Schedule Registration
```php
// routes/console.php or bootstrap/app.php
Schedule::command('reminders:send-2h')->everyFifteenMinutes();
```

**Why 15 minutes?** The 2-hour reminder uses a 7-minute window (tighter than 24h's 30-minute window) because timing is more critical close to the appointment. Running every 15 minutes ensures consultations are caught within the window while balancing server load.

## Edge Cases & Error Handling

| Scenario | Handling |
|----------|----------|
| Notification fails to send | Queue will retry; failed jobs logged to `failed_jobs` table |
| Consultation rescheduled after 24h reminder but before 2h | New datetime will trigger 2h reminder (tracking column is separate) |
| Consultation cancelled after reminder sent | No action needed - reminder already sent |
| User has no email | Notification skipped (Laravel handles gracefully) |
| Timezone considerations | All times stored/compared in app timezone (configured in `config/app.php`) |
| 24h reminder not sent (e.g., booking made same day) | 2h reminder still sends independently |
| Consultation scheduled less than 2 hours away | Won't receive 2h reminder (outside window) |

## Test Scenarios

### Unit Tests (`tests/Unit/Commands/Send2HourRemindersTest.php`)
```php
test('command finds consultations approximately 2 hours away', function () {
    // Create consultation 2 hours from now
    // Run command
    // Assert notification sent
    // Assert reminder_2h_sent_at is set
});

test('command skips consultations with reminder already sent', function () {
    // Create consultation with reminder_2h_sent_at already set
    // Run command
    // Assert no notification sent
});

test('command skips non-approved consultations', function () {
    // Create cancelled, no-show, pending, completed consultations
    // Run command
    // Assert no notifications sent
});

test('command uses 7-minute window for matching', function () {
    // Create consultation at exactly 2h + 8 minutes (outside window)
    // Run command
    // Assert no notification sent

    // Create consultation at exactly 2h + 6 minutes (inside window)
    // Run command
    // Assert notification sent
});

test('command only checks consultations scheduled for today', function () {
    // Create consultation 2 hours from now but tomorrow's date
    // Run command
    // Assert no notification sent
});
```

### Feature Tests (`tests/Feature/Notifications/ConsultationReminder2hTest.php`)
```php
test('reminder email contains correct consultation details', function () {
    // Create consultation
    // Send notification
    // Assert email contains date, time
});

test('final payment reminder shown for unpaid paid consultations', function () {
    // Create paid consultation with payment_status = 'pending'
    // Assert email contains payment reminder section
});

test('payment reminder hidden when payment received', function () {
    // Create paid consultation with payment_status = 'received'
    // Assert email does NOT contain payment reminder
});

test('email uses client preferred language', function () {
    // Create user with preferred_language = 'en'
    // Assert email template is English version
});

test('email includes office contact information', function () {
    // Send notification
    // Assert email contains contact phone/email
});

test('email does not include calendar download link', function () {
    // Send notification
    // Assert email does NOT contain calendar route
});
```

## Files to Create/Modify

| File | Action |
|------|--------|
| `app/Console/Commands/Send2HourReminders.php` | CREATE |
| `app/Notifications/ConsultationReminder2h.php` | CREATE |
| `resources/views/emails/reminders/consultation-2h/ar.blade.php` | CREATE |
| `resources/views/emails/reminders/consultation-2h/en.blade.php` | CREATE |
| `routes/console.php` | MODIFY (add schedule) |
| `tests/Unit/Commands/Send2HourRemindersTest.php` | CREATE |
| `tests/Feature/Notifications/ConsultationReminder2hTest.php` | CREATE |

**Note:** Migration for `reminder_2h_sent_at` column is handled in Story 8.6.

## Definition of Done
- [ ] Artisan command `reminders:send-2h` created and works
- [ ] Command scheduled to run every 15 minutes
- [ ] Notification class implements `ShouldQueue`
- [ ] Reminders only sent for approved consultations within 2h window (7-min tolerance)
- [ ] No duplicate reminders (tracking column `reminder_2h_sent_at` updated)
- [ ] Payment reminder shown only when `paid` AND `payment_status != 'received'`
- [ ] Contact information for last-minute issues included
- [ ] Bilingual email templates (Arabic/English)
- [ ] All unit and feature tests pass
- [ ] Code formatted with `vendor/bin/pint`

## References
- **PRD Section 5.4:** Email Notifications - "Consultation reminder (2 hours before)"
- **PRD Section 8.2:** Email Templates - Template requirements and branding
- **Story 8.1:** Base email template and queue configuration
- **Story 8.6:** Migration for reminder columns, similar command/notification pattern

## Estimation
**Complexity:** Medium | **Effort:** 2-3 hours
