## Overview

A lightweight notification system that sends email alerts to designated recipients when iPaaS flows complete with any failures. The system provides flow-level recipient management with tenant-level configuration options and automatic failure detection.

## Core Requirements

### Trigger Conditions
- **Primary Trigger**: Any ProcessNode job failure during flow execution
- **Timing**: Send notification once after flow completion (regardless of success/failure mix)
- **Scope**: Per flow execution (run_id isolation)

### Recipients
- **Flow-level Recipients**: Multiple users per flow
- **Support Types**:
  - Tenant users (auto-removal on user deletion)
  - External email addresses (manual removal only)
- **Fallback**: Tenant-level default email for flows without specific recipients

### Notification Content
- Flow name and execution summary
- Start and end timestamps
- Total records processed
- Success/failure counts
- Direct link to flow execution details

---

## Database Schema

### New Tables

#### `flow_notification_recipients`
```sql
CREATE TABLE flow_notification_recipients (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    flow_id BIGINT UNSIGNED NOT NULL,
    user_id BIGINT UNSIGNED NULL,           -- NULL for external emails
    email VARCHAR(255) NULL,                -- NULL for user recipients
    is_external BOOLEAN DEFAULT FALSE,      -- TRUE for external emails
    created_at TIMESTAMP NULL DEFAULT NULL,
    updated_at TIMESTAMP NULL DEFAULT NULL,

    INDEX idx_flow_recipients (flow_id),
    INDEX idx_user_recipients (user_id),
    CONSTRAINT UNIQUE unique_flow_user (flow_id, user_id),
    CONSTRAINT UNIQUE unique_flow_email (flow_id, email),
    CONSTRAINT CHECK (
        (user_id IS NOT NULL AND email IS NULL AND is_external = FALSE) OR
        (user_id IS NULL AND email IS NOT NULL AND is_external = TRUE)
    )
);
```

#### `tenant_notification_settings`
```sql
CREATE TABLE tenant_notification_settings (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    tenant_id VARCHAR(255) NOT NULL UNIQUE,
    notifications_enabled BOOLEAN DEFAULT TRUE,
    fallback_email VARCHAR(255) NULL,
    created_at TIMESTAMP NULL DEFAULT NULL,
    updated_at TIMESTAMP NULL DEFAULT NULL,

    INDEX idx_tenant_settings (tenant_id)
);
```

---

## System Architecture

### Component Overview

```
Flow Completion Event
    ↓
FlowNotificationService
    ↓
Recipient Resolution
    ↓
Failure Detection
    ↓
Email Dispatch
    ↓
Notification Sent
```

### Key Components

#### 1. FlowNotificationService
**Purpose**: Central service for managing flow completion notifications

**Responsibilities**:
- Listen for flow completion events
- Analyze flow metrics for failures
- Resolve notification recipients
- Dispatch notification emails
- Handle tenant-level configuration

#### 2. FlowNotificationRecipient Model
**Purpose**: Manage flow-level notification recipients

**Relationships**:
- `belongsTo(Flow::class)`
- `belongsTo(User::class)` (nullable)

#### 3. TenantNotificationSettings Model
**Purpose**: Manage tenant-level notification configuration

**Scope**: Tenant-specific settings and fallback configuration

#### 4. FlowNotificationMail
**Purpose**: Email template for flow completion notifications

**Content**:
- Flow execution summary
- Failure details
- Navigation links

---

## Integration Points

### Flow Completion Detection

**Hook Point**: Flow completion events (not dual-counter system)

```php
// Enhanced flow completion event
Event::dispatch(new FlowCompleted([
    'flow_id' => $flowId,
    'run_id' => $runId,
    'tenant_database' => $tenantDatabase,
    'started_at' => $startTime,
    'completed_at' => now(),
    'total_records' => $totalRecords,
    'failed_records' => $failedRecords,
    'success_records' => $successRecords
]));
```

### Metrics Enhancement

**Current Gap**: Flow metrics may not accurately track failed records

**Enhancement Required**:
- Track ProcessNode failures in FlowMetricsService
- Store failure count alongside success metrics
- Ensure failure detection works across paginated flows

```php
// Enhanced metrics collection
FlowMetricsService::recordNodeFailure($flowId, $runId, $nodeId, $error);
FlowMetricsService::getFailureCount($flowId, $runId);
```

---

## User Interface

### Flow Recipients Management

#### Access Points
1. **Flow Detail Page**: "Manage Notifications" button
2. **Flow Index Table**: Action dropdown menu item

#### Modal Interface
**Recipients List**:
- Display current recipients (users and external emails)
- User recipients show name + email
- External recipients show email only
- Remove action for each recipient

**Add Recipients**:
- Dropdown for tenant users
- Text input for external email addresses
- Validation for email format and duplicates

#### Wireframe Concept
```
┌─────────────────────────────────────────┐
│ Manage Flow Notifications               │
├─────────────────────────────────────────┤
│ Current Recipients:                     │
│ • John Doe (john@company.com)    [×]    │
│ • external@partner.com           [×]    │
│                                         │
│ Add Recipients:                         │
│ ┌─ Select User ────────────────────┐    │
│ │ [Dropdown: Tenant Users]         │    │
│ └──────────────────────────────────┘    │
│                                         │
│ ┌─ External Email ─────────────────┐    │
│ │ [Input: email@example.com]       │    │
│ └──────────────────────────────────┘    │
│                                         │
│ [Add User] [Add Email]                  │
│                                         │
│ [Cancel] [Save Changes]                 │
└─────────────────────────────────────────┘
```

### Tenant Settings

#### Admin Settings Page
**Section**: "Notification Settings"

**Options**:
- Enable/disable all iPaaS notifications
- Set fallback email for flows without recipients
- Test notification functionality

---

## Email Template Design

### Subject Line
```
"Flow Execution Alert: [Flow Name] - [X] Records Failed"
```

### Email Content
```html
<h2>Flow Execution Summary</h2>

<div class="flow-details">
    <h3>[Flow Name]</h3>
    <p><strong>Execution Time:</strong> [Start Time] - [End Time]</p>
    <p><strong>Duration:</strong> [X minutes Y seconds]</p>
</div>

<div class="execution-metrics">
    <h3>Processing Results</h3>
    <ul>
        <li><strong>Total Records:</strong> [X]</li>
        <li><strong>Successful:</strong> [Y]</li>
        <li><strong>Failed:</strong> [Z]</li>
    </ul>
</div>

<div class="actions">
    <a href="[Flow URL]" class="button">View Flow Details</a>
    <a href="[Execution URL]" class="button">View Execution Log</a>
</div>

<div class="footer">
    <p>This notification was sent because the flow completed with record failures.</p>
    <p><a href="[Unsubscribe URL]">Manage notification settings</a></p>
</div>
```

---

## Technical Implementation

### Service Layer

#### FlowNotificationService
```php
class FlowNotificationService
{
    public function handleFlowCompletion(FlowCompleted $event): void
    {
        // Check if notifications are enabled for tenant
        if (!$this->areNotificationsEnabled($event->tenantDatabase)) {
            return;
        }

        // Check if flow had any failures
        if (!$this->hasFailures($event)) {
            return;
        }

        // Get recipients for flow
        $recipients = $this->getFlowRecipients($event->flow_id, $event->tenantDatabase);

        // Send notifications
        $this->sendNotifications($recipients, $event);
    }

    private function getFlowRecipients(int $flowId, string $tenantDatabase): Collection
    {
        // Get flow-specific recipients
        $recipients = FlowNotificationRecipient::where('flow_id', $flowId)->get();

        // Add fallback email if no recipients
        if ($recipients->isEmpty()) {
            $fallbackEmail = $this->getTenantFallbackEmail($tenantDatabase);
            if ($fallbackEmail) {
                $recipients->push(new FlowNotificationRecipient([
                    'email' => $fallbackEmail,
                    'is_external' => true
                ]));
            }
        }

        return $recipients;
    }

    private function hasFailures(FlowCompleted $event): bool
    {
        return $event->failed_records > 0;
    }
}
```

### Event Listener
```php
class FlowCompletionNotificationListener
{
    public function handle(FlowCompleted $event): void
    {
        app(FlowNotificationService::class)->handleFlowCompletion($event);
    }
}
```

### Model Relationships

#### FlowNotificationRecipient
```php
class FlowNotificationRecipient extends Model
{
    use HasFactory;

    protected $connection = 'tenant_connection';

    protected $fillable = [
        'flow_id', 'user_id', 'email', 'is_external'
    ];

    protected $casts = [
        'is_external' => 'boolean'
    ];

    public function flow(): BelongsTo
    {
        return $this->belongsTo(Flow::class);
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    public function getRecipientEmailAttribute(): string
    {
        return $this->is_external ? $this->email : $this->user->email;
    }

    public function getRecipientNameAttribute(): string
    {
        return $this->is_external ? $this->email : $this->user->name;
    }
}
```

---

## Migration Plan

### Phase 1: Database Schema
1. Create `flow_notification_recipients` table migration
2. Create `tenant_notification_settings` table migration
3. Run tenant migrations: `php artisan migrate:tenants`

### Phase 2: Core Service Implementation
1. Implement FlowNotificationService
2. Create FlowNotificationRecipient model
3. Create TenantNotificationSettings model
4. Enhance FlowMetricsService for failure tracking

### Phase 3: Event Integration
1. Enhance flow completion events with failure data
2. Create FlowCompletionNotificationListener
3. Register event listener in EventServiceProvider

### Phase 4: Email System
1. Create FlowNotificationMail mailable
2. Design email template
3. Implement queue-based email sending

### Phase 5: User Interface
1. Create recipient management modal component
2. Add notification management to flow interfaces
3. Create tenant notification settings page
4. Implement recipient CRUD operations

---

## Data Cleanup Strategy

### User Deletion Cascade
```php
// In User model deletion event
class UserObserver
{
    public function deleting(User $user): void
    {
        // Remove user from all flow notification recipients
        FlowNotificationRecipient::where('user_id', $user->id)->delete();
    }
}
```

### Orphaned Data Cleanup
- External email recipients persist independently
- Manual removal through UI only
- No automatic cleanup for external addresses

---

## Error Handling

### Notification Failures
- Queue email notifications for reliability
- Log notification failures without blocking flow completion
- Implement retry logic for failed email sends
- Graceful degradation when email service unavailable

### Edge Cases
- Handle missing tenant notification settings (default enabled)
- Manage invalid email addresses gracefully
- Handle flows with zero records processed
- Manage concurrent flow executions safely

---

## Security Considerations

### Email Validation
- Validate external email addresses on input
- Prevent email injection attacks
- Rate limit notification subscription changes

### Access Control
- Only tenant users can manage flow recipients
- Tenant isolation for all notification data
- Verify flow ownership before recipient changes

### Data Privacy
- No sensitive payload data in notifications
- Secure unsubscribe mechanisms
- GDPR compliance for external email storage

---

## Performance Considerations

### Optimization Strategies
- Index flow_id and user_id columns
- Cache tenant notification settings
- Queue email notifications asynchronously
- Batch email sends when possible

### Scalability
- Designed for tenant-level volume (dozens of flows)
- Email queue prevents blocking flow completion
- Minimal database queries per notification

---

## Testing Strategy

### Unit Tests
- FlowNotificationService logic
- Recipient resolution algorithms
- Email content generation
- Model relationships and constraints

### Integration Tests
- End-to-end notification flow
- Event listener integration
- Database constraint validation
- Email queue processing

### UI Tests
- Recipient management modal
- Tenant settings interface
- Form validation and error handling

---

## Definition of Done

### Core Functionality
- [ ] Flow failures trigger notifications after completion
- [ ] Support both user and external email recipients
- [ ] Tenant-level notification enable/disable setting
- [ ] Fallback email for flows without specific recipients
- [ ] Single notification per flow execution with failures

### Database Schema
- [ ] `flow_notification_recipients` table created with proper constraints
- [ ] `tenant_notification_settings` table created
- [ ] Foreign key relationships established
- [ ] User deletion cascades remove associated recipients
- [ ] Migration files in `database/migrations/tenants/`

### User Interface
- [ ] Recipient management modal accessible from flow detail and index
- [ ] Add/remove user recipients via dropdown selection
- [ ] Add/remove external email recipients via text input
- [ ] Tenant notification settings in admin interface
- [ ] Form validation prevents duplicate recipients
- [ ] Email format validation for external addresses

### Email System
- [ ] Professional email template with flow summary
- [ ] Subject line includes flow name and failure count
- [ ] Email contains start/end timestamps and record counts
- [ ] Direct links to flow and execution details
- [ ] Unsubscribe/management links included
- [ ] Emails queued for reliable delivery

### Integration
- [ ] Hooks into existing flow completion events
- [ ] Enhanced FlowMetricsService tracks failure counts
- [ ] Does not depend on dual-counter system
- [ ] Works with paginated and non-paginated flows
- [ ] Proper tenant database isolation

### Error Handling
- [ ] Notification failures don't block flow completion
- [ ] Invalid email addresses handled gracefully
- [ ] Missing tenant settings default to enabled
- [ ] Logging for troubleshooting notification issues

### Performance & Security
- [ ] Database queries optimized with proper indexes
- [ ] Email notifications processed asynchronously
- [ ] Input validation prevents injection attacks
- [ ] Tenant isolation enforced at all levels
- [ ] Rate limiting on subscription changes

### Testing
- [ ] Unit tests cover service logic and models
- [ ] Integration tests verify end-to-end functionality
- [ ] UI tests validate form behavior and validation
- [ ] Test database schema constraints and cascades
- [ ] Test notification triggering for various failure scenarios

---

## Acceptance Criteria

### Functional Criteria

#### Notification Triggering
1. **GIVEN** a flow execution completes with any ProcessNode failures
   **WHEN** the flow completion event is fired
   **THEN** notification emails are sent to all configured recipients

2. **GIVEN** a flow execution completes with zero failures
   **WHEN** the flow completion event is fired
   **THEN** no notification emails are sent

3. **GIVEN** a flow has no configured recipients
   **WHEN** the flow completes with failures
   **THEN** notification is sent to tenant fallback email (if configured)

#### Recipient Management
4. **GIVEN** a user has permission to manage a flow
   **WHEN** they access the notification management modal
   **THEN** they can view, add, and remove recipients

5. **GIVEN** a tenant user is deleted from the system
   **WHEN** the deletion occurs
   **THEN** they are automatically removed from all flow recipient lists

6. **GIVEN** an external email is added as a recipient
   **WHEN** stored in the database
   **THEN** it persists until manually removed by a user

#### Tenant Configuration
7. **GIVEN** a tenant admin accesses notification settings
   **WHEN** they disable iPaaS notifications
   **THEN** no flow notification emails are sent for that tenant

8. **GIVEN** a tenant sets a fallback email address
   **WHEN** flows without specific recipients complete with failures
   **THEN** notifications are sent to the fallback email

### Technical Criteria

#### Database Integration
9. **GIVEN** the notification system is implemented
   **WHEN** database migrations are run
   **THEN** all tables are created in the tenant database with proper constraints

10. **GIVEN** multiple recipients are configured for a flow
    **WHEN** stored in the database
    **THEN** duplicate user/email combinations are prevented by constraints

#### Email Delivery
11. **GIVEN** a flow completion notification is triggered
    **WHEN** the email is generated
    **THEN** it contains flow name, timestamps, record counts, and navigation links

12. **GIVEN** multiple recipients exist for a failed flow
    **WHEN** notifications are sent
    **THEN** each recipient receives exactly one email per flow execution

#### Performance & Reliability
13. **GIVEN** the notification system is active
    **WHEN** flows complete
    **THEN** notification processing does not block or delay flow completion

14. **GIVEN** email delivery fails
    **WHEN** the failure occurs
    **THEN** the error is logged and flow completion continues normally

#### User Experience
15. **GIVEN** a user manages flow recipients
    **WHEN** they interact with the interface
    **THEN** changes are immediately reflected and validated

16. **GIVEN** invalid email addresses are entered
    **WHEN** the form is submitted
    **THEN** clear validation errors are displayed to the user

---

# Required updates -- 2025.09.17
**Author**: Quinn Johns

### High
- Notification enablement defaults to “enabled” on errors
  - **File**: `src/Domain/Ipaas/Notifications/Services/FlowNotificationService.php`
  - **Direction**: Change `areNotificationsEnabled()` to fail closed on settings read errors (return false) or add a guarded retry/circuit-breaker. Log clearly when defaulting so unexpected emails don’t go out due to config failures.

### Medium
- Critical failure subject shows “-1 Records Failed”
  - **File**: `src/App/Mail/FlowNotificationMail.php`
  - **Direction**: Detect critical failures (e.g., `failed_records === -1` or an explicit flag) and use a distinct subject/body (“Critical Flow Failure”) rather than interpolating -1. Currently, the mailable subject uses the number directly, producing “-1 Records Failed”.

- Mail queued on “default” queue
  - **File**: `src/App/Mail/FlowNotificationMail.php` (and optionally `config/queue.php` or env)
  - **Direction**: Route the mailable to a dedicated mail queue (e.g., `mail`) to ensure predictable delivery and isolation from heavy default workloads.

- Metrics readiness race can suppress notifications
  - **File**: `src/App/Observers/FlowCompletedObserver.php` (only as it gates mail), `src/Domain/Ipaas/Metrics/Services/FlowMetricsService.php`
  - **Direction**: Ensure metrics are finalized before evaluating failures (e.g., finalize counters in one method, or poll/await run-scoped “final metrics” flag) so `failed_records` is accurate when deciding to send mail.

### Add unit test for these assertions

Feel free to leverage Cursor here. Provide it the `ai_rules.md` and `ai_test.md` files. Tell it to "read those files and apply it to your conversation", and then feed it these assertions. Feel free to add your own assertions as well.

- Ensure no mail when notifications disabled
  - Assert: With tenant notifications disabled, `Mail::fake()` then `FlowNotificationService::handleFlowCompletion($event)` results in `Mail::assertNothingQueued()`.

- Ensure no mail when there are no failures
  - Assert: With `failed_records` absent or `0`, nothing is queued: `Mail::assertNothingQueued()`.

- Ensure mail is queued when failures exist and recipients configured
  - Assert: With `failed_records > 0` and one user-recipient, exactly one mailable is queued: `Mail::assertQueued(FlowNotificationMail::class, 1)`.
  - Assert (closure): recipient email matches the user’s email; `$mail->flowData['id'] === $flowId`; `$mail->eventData->run_id === $event->run_id`; `$mail->queue === 'default'`.
  - Assert (subject semantics): subject contains the flow name and the failed count (e.g., “Records Failed 3”).

- External recipient path
  - Assert: With an external recipient (no user), queued count is 1 and the queued mailable recipient email equals the external address.

- Fallback recipient when no flow recipients
  - Assert: With no recipients configured and a tenant fallback email set, exactly one mailable is queued to the fallback address.

- No recipients and no fallback
  - Assert: With no recipients and no fallback email, `Mail::assertNothingQueued()`.

- Multiple recipients fan-out
  - Assert: With N recipients (mix of user/external), `Mail::assertQueued(FlowNotificationMail::class, N)` and each closure validates distinct recipient emails match the configured set.

- Critical failure semantics
  - Current behavior: With `failed_records === -1`, assert one mailable queued and subject includes “-1 Records Failed”.
  - If you implement the “Critical Flow Failure” subject improvement later, update the assertion to expect the critical string instead.

- Event payload invariants passed into the mailable
  - Assert (closure): `$mail->eventData` contains `failed_records`, `successful_records`, `total_records`, `started_at`, `completed_at`, `run_id`, and `tenant_database` matching the event used.

- Settings read error behavior (document and assert the chosen policy)
  - Current behavior: If settings lookup throws, default is enabled → assert mail IS queued when failures exist.
  - If you switch to fail-closed: assert `Mail::assertNothingQueued()` on settings error.

- “Flow not found” resilience
  - Assert: When one recipient refers to a missing `flow_id`, that recipient yields no queued mail but other valid recipients still queue successfully (total queued equals valid recipients).

### Static type checking
Ensure you run `phpstan level 5` on any of the files you created/altered for this task. Cursor can be helpful here.
