# Story 8.8: Timeline Update Notification

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

## Story Context
This story implements email notifications when admin adds updates to a client's case timeline (from Epic 4). When admin creates a `TimelineUpdate` record, the associated client automatically receives an email with the update details, keeping them informed of their case progress without needing to manually check the portal.

## User Story
As a **client**,
I want **to be notified via email when my case timeline is updated**,
So that **I stay informed about my case progress without having to repeatedly check the portal**.

## Dependencies
- **Requires**: Story 8.1 (Email Infrastructure - `BaseMailable` class and templates)
- **Requires**: Epic 4 Stories 4.1-4.2 (Timeline and TimelineUpdate models must exist)
- **Blocks**: None

## Data Model Reference

From Epic 4, the relevant models are:

```
Timeline
├── id
├── user_id (FK → users.id, the client)
├── case_name (string, required)
├── case_reference (string, optional, unique if provided)
├── status (enum: 'active', 'archived')
├── created_at
└── updated_at

TimelineUpdate
├── id
├── timeline_id (FK → timelines.id)
├── admin_id (FK → users.id, the admin who created it)
├── update_text (text, the update content)
├── created_at
└── updated_at

Relationships:
- TimelineUpdate belongsTo Timeline
- Timeline belongsTo User (client)
- Timeline hasMany TimelineUpdate
- Access client: $timelineUpdate->timeline->user
```

## Acceptance Criteria

### Trigger
- [ ] Email sent automatically when `TimelineUpdate` is created
- [ ] Uses model observer pattern for clean separation
- [ ] Email queued for performance (not sent synchronously)
- [ ] Only triggered for active timelines (not archived)

### Content
- [ ] Subject: "Update on your case: [Case Name]" / "تحديث على قضيتك: [اسم القضية]"
- [ ] Case reference number (if exists)
- [ ] Full update content text
- [ ] Date of update (formatted for locale)
- [ ] "View Timeline" button linking to client dashboard timeline view

### Language
- [ ] Email template selected based on client's `preferred_language`
- [ ] Default to Arabic ('ar') if no preference set
- [ ] Date formatting appropriate for locale

### Design
- [ ] Uses base email template from Story 8.1 (Libra branding)
- [ ] Professional, informative tone
- [ ] Clear visual hierarchy: case name → update content → action button

## Technical Implementation

### Files to Create/Modify

| File | Action | Description |
|------|--------|-------------|
| `app/Mail/TimelineUpdateEmail.php` | Create | Mailable extending BaseMailable |
| `resources/views/emails/timeline/update-ar.blade.php` | Create | Arabic email template |
| `resources/views/emails/timeline/update-en.blade.php` | Create | English email template |
| `app/Observers/TimelineUpdateObserver.php` | Create | Observer to trigger email |
| `app/Providers/AppServiceProvider.php` | Modify | Register the observer |

### Mailable Implementation

```php
<?php

namespace App\Mail;

use App\Models\TimelineUpdate;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;

class TimelineUpdateEmail extends BaseMailable
{
    public function __construct(
        public TimelineUpdate $update
    ) {
        $this->locale = $this->update->timeline->user->preferred_language ?? 'ar';
    }

    public function envelope(): Envelope
    {
        $caseName = $this->update->timeline->case_name;
        $subject = $this->locale === 'ar'
            ? "تحديث على قضيتك: {$caseName}"
            : "Update on your case: {$caseName}";

        return new Envelope(
            subject: $subject,
        );
    }

    public function content(): Content
    {
        return new Content(
            markdown: "emails.timeline.update-{$this->locale}",
            with: [
                'update' => $this->update,
                'timeline' => $this->update->timeline,
                'client' => $this->update->timeline->user,
                'viewUrl' => route('client.timelines.show', $this->update->timeline),
            ],
        );
    }
}
```

### Observer Implementation

```php
<?php

namespace App\Observers;

use App\Mail\TimelineUpdateEmail;
use App\Models\TimelineUpdate;
use Illuminate\Support\Facades\Mail;

class TimelineUpdateObserver
{
    public function created(TimelineUpdate $update): void
    {
        // Only send for active timelines
        if ($update->timeline->status !== 'active') {
            return;
        }

        $client = $update->timeline->user;

        Mail::to($client->email)->queue(
            new TimelineUpdateEmail($update)
        );
    }
}
```

### Register Observer

In `AppServiceProvider::boot()`:

```php
use App\Models\TimelineUpdate;
use App\Observers\TimelineUpdateObserver;

public function boot(): void
{
    TimelineUpdate::observe(TimelineUpdateObserver::class);
}
```

### Arabic Template Structure (`update-ar.blade.php`)

```blade
<x-mail::message>
# تحديث على قضيتك

**اسم القضية:** {{ $timeline->case_name }}
@if($timeline->case_reference)
**رقم المرجع:** {{ $timeline->case_reference }}
@endif

---

## التحديث

{{ $update->update_text }}

**تاريخ التحديث:** {{ $update->created_at->locale('ar')->isoFormat('LL') }}

<x-mail::button :url="$viewUrl">
عرض الجدول الزمني
</x-mail::button>

مع تحياتنا,<br>
{{ config('app.name') }}
</x-mail::message>
```

### English Template Structure (`update-en.blade.php`)

```blade
<x-mail::message>
# Update on Your Case

**Case Name:** {{ $timeline->case_name }}
@if($timeline->case_reference)
**Reference:** {{ $timeline->case_reference }}
@endif

---

## Update

{{ $update->update_text }}

**Date:** {{ $update->created_at->format('F j, Y') }}

<x-mail::button :url="$viewUrl">
View Timeline
</x-mail::button>

Best regards,<br>
{{ config('app.name') }}
</x-mail::message>
```

## Edge Cases & Error Handling

| Scenario | Handling |
|----------|----------|
| Archived timeline gets update | No email sent (observer checks status) |
| Client has no `preferred_language` | Default to Arabic ('ar') |
| Client email is null/invalid | Mail will fail gracefully, logged to failed_jobs |
| Timeline has no case_reference | Template conditionally hides reference line |
| Multiple rapid updates | Each triggers separate email (acceptable per requirements) |

## Testing Requirements

### Test File
Create `tests/Feature/Mail/TimelineUpdateEmailTest.php`

### Test Scenarios

```php
<?php

use App\Mail\TimelineUpdateEmail;
use App\Models\Timeline;
use App\Models\TimelineUpdate;
use App\Models\User;
use Illuminate\Support\Facades\Mail;

test('email is queued when timeline update is created', function () {
    Mail::fake();

    $client = User::factory()->create(['preferred_language' => 'en']);
    $timeline = Timeline::factory()->for($client)->create(['status' => 'active']);

    TimelineUpdate::factory()->for($timeline)->create();

    Mail::assertQueued(TimelineUpdateEmail::class);
});

test('email is not sent for archived timeline updates', function () {
    Mail::fake();

    $client = User::factory()->create();
    $timeline = Timeline::factory()->for($client)->create(['status' => 'archived']);

    TimelineUpdate::factory()->for($timeline)->create();

    Mail::assertNothingQueued();
});

test('email uses arabic template when client prefers arabic', function () {
    $client = User::factory()->create(['preferred_language' => 'ar']);
    $timeline = Timeline::factory()->for($client)->create();
    $update = TimelineUpdate::factory()->for($timeline)->create();

    $mailable = new TimelineUpdateEmail($update);

    expect($mailable->locale)->toBe('ar');
});

test('email uses english template when client prefers english', function () {
    $client = User::factory()->create(['preferred_language' => 'en']);
    $timeline = Timeline::factory()->for($client)->create();
    $update = TimelineUpdate::factory()->for($timeline)->create();

    $mailable = new TimelineUpdateEmail($update);

    expect($mailable->locale)->toBe('en');
});

test('email defaults to arabic when no language preference', function () {
    $client = User::factory()->create(['preferred_language' => null]);
    $timeline = Timeline::factory()->for($client)->create();
    $update = TimelineUpdate::factory()->for($timeline)->create();

    $mailable = new TimelineUpdateEmail($update);

    expect($mailable->locale)->toBe('ar');
});

test('email contains case name in subject', function () {
    $client = User::factory()->create(['preferred_language' => 'en']);
    $timeline = Timeline::factory()->for($client)->create(['case_name' => 'Smith vs Jones']);
    $update = TimelineUpdate::factory()->for($timeline)->create();

    $mailable = new TimelineUpdateEmail($update);

    expect($mailable->envelope()->subject)->toContain('Smith vs Jones');
});

test('email contains update content', function () {
    $client = User::factory()->create(['preferred_language' => 'en']);
    $timeline = Timeline::factory()->for($client)->create();
    $update = TimelineUpdate::factory()->for($timeline)->create([
        'update_text' => 'Court date scheduled for next month.',
    ]);

    $mailable = new TimelineUpdateEmail($update);

    $mailable->assertSeeInHtml('Court date scheduled for next month.');
});

test('email contains view timeline link', function () {
    $client = User::factory()->create(['preferred_language' => 'en']);
    $timeline = Timeline::factory()->for($client)->create();
    $update = TimelineUpdate::factory()->for($timeline)->create();

    $mailable = new TimelineUpdateEmail($update);

    $mailable->assertSeeInHtml(route('client.timelines.show', $timeline));
});
```

## Definition of Done
- [ ] `TimelineUpdateEmail` mailable created extending `BaseMailable`
- [ ] Arabic and English templates created with proper formatting
- [ ] Observer registered and triggers on `TimelineUpdate` creation
- [ ] Email only sent for active timelines (not archived)
- [ ] Email queued (not sent synchronously)
- [ ] Subject includes case name in appropriate language
- [ ] Email body includes case reference (if exists), update content, and date
- [ ] View Timeline button links to correct client dashboard route
- [ ] All tests pass
- [ ] Code formatted with Pint

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