# Profile Auto-Delete System Reference

This document provides comprehensive documentation on the profile auto-delete system, including configuration, commands, workflows, and testing procedures.

## Table of Contents
1. [Overview](#overview)
2. [System Architecture](#system-architecture)
3. [Configuration Structure](#configuration-structure)
4. [Artisan Commands](#artisan-commands)
5. [Workflow Process](#workflow-process)
6. [Scheduled Execution](#scheduled-execution)
7. [Testing](#testing)
8. [Production Configuration](#production-configuration)
9. [Troubleshooting](#troubleshooting)
10. [Related Systems](#related-systems)

---

## Overview

The profile auto-delete system automatically manages inactive profiles through a two-step process:

1. **Mark Inactive**: Profiles that haven't logged in for a configured period are automatically marked as inactive
2. **Process Inactive**: Inactive profiles receive progressive warning emails before final deletion

### Purpose
- Maintain platform health by removing abandoned profiles
- Protect user privacy by deleting old personal data
- Send warning emails giving users a chance to prevent deletion
- Comply with data retention policies and GDPR requirements
- Keep the active user base current and engaged

### Key Features
- **Two-step process**: Separate inactive marking from deletion
- **Progressive warnings**: 3 escalating warning emails before deletion
- **Grace period**: Deleted profiles can be restored within configurable timeframe (default: 30 days)
- **Configurable thresholds**: All timeframes configurable per platform
- **Balance protection**: Prevents deletion of profiles with negative balances
- **Transaction anonymization**: Past transactions preserved but anonymized
- **Balance handling**: Multiple options for account balance disposal
- **Automatic scheduling**: Runs daily via Laravel scheduler
- **Comprehensive logging**: All actions logged for audit trail

---

## System Architecture

### Two-Step Process

#### Step 1: Mark Inactive (Daily at 01:30)
```
┌─────────────────────────────────────────────────────────────┐
│ profiles:mark-inactive                                       │
├─────────────────────────────────────────────────────────────┤
│ • Finds profiles where last_login_at > days_not_logged_in   │
│ • Sets inactive_at = now()                                   │
│ • Profile becomes hidden/labeled (per config)                │
│ • Logs action to mark-inactive-profiles.log                  │
└─────────────────────────────────────────────────────────────┘
```

#### Step 2: Process Inactive (Daily at 02:00)
```
┌─────────────────────────────────────────────────────────────┐
│ profiles:process-inactive                                    │
├─────────────────────────────────────────────────────────────┤
│ • Finds profiles where inactive_at is set                    │
│ • Calculates days since inactive_at                          │
│ • Sends appropriate warning based on thresholds:             │
│   - Warning 1: X days after inactive                         │
│   - Warning 2: Y days after inactive                         │
│   - Final Warning: Z days after inactive                     │
│   - Delete: N days after inactive                            │
│ • Logs all actions to inactive-profiles.log                  │
└─────────────────────────────────────────────────────────────┘
```

### Database Schema

**Users/Organizations Table:**
- `last_login_at` (timestamp, nullable) - Last successful login
- `inactive_at` (timestamp, nullable) - When profile was marked inactive
- `deleted_at` (timestamp, nullable) - Soft delete timestamp

**Field States:**
- `inactive_at = NULL` → Profile is active
- `inactive_at = [past date]` → Profile is inactive, awaiting deletion
- `deleted_at = [date]` → Profile is soft-deleted

---

## Configuration Structure

### Location
Configuration is defined in:
- `config/timebank-default.php` (lines 1405-1420+)
- `config/timebank_cc.php` (lines 1405-1420+)
- Active config loaded via `TIMEBANK_CONFIG` environment variable

### Complete Configuration

```php
// Step 1: Mark profiles as inactive
'profile_inactive' => [
    'days_not_logged_in' => 350,     // Profile marked inactive after this many days
    're-activate_at_login' => true,  // Auto-reactivate when user logs in
    'messenger_hidden' => true,      // Hide from messenger search
    'profile_search_hidden' => true, // Hide from main search
    'profile_hidden' => true,        // Hide profile page from non-admins
    'profile_labeled' => true,       // Show "Inactive" label
],

// Step 2: Process inactive profiles (send warnings and delete)
'delete_profile' => [
    // NOTE: These are days AFTER the profile is marked inactive
    // Total time = days_not_logged_in + days_after_inactive
    'days_after_inactive' => [
        'warning_1' => 7,      // First warning
        'warning_2' => 10,     // Second warning
        'warning_final' => 14, // Final warning
        'run_delete' => 15,    // Delete profile
    ],
    'account_balances' => [
        // Handle account balances on deletion (elsif logic):
        'donate_balances_to_organization_account_specified' => true,
        'transfer_balances_to_bank_client' => false,
        'transfer_balances_to_account_id' => null,
        'transfer_balances_to_debit_account' => true,
    ],
],
```

### Configuration Timeline Example

With the configuration above:
- **Day 0**: User last logs in
- **Day 350**: Profile marked inactive (`profiles:mark-inactive`)
- **Day 357**: First warning email sent (350 + 7)
- **Day 360**: Second warning email sent (350 + 10)
- **Day 364**: Final warning email sent (350 + 14)
- **Day 365**: Profile deleted (350 + 15)

**Total: 1 year from last login to deletion**

---

## Artisan Commands

### 1. Mark Inactive Profiles

**Command:** `php artisan profiles:mark-inactive`

**Purpose:** Automatically mark profiles as inactive based on login inactivity.

**What It Does:**
1. Finds all User and Organization profiles where:
   - `last_login_at` is older than configured `days_not_logged_in`
   - `inactive_at` is NULL (not already marked inactive)
2. Sets `inactive_at` to current timestamp
3. Logs action to `storage/logs/mark-inactive-profiles.log`

**Usage:**
```bash
# Mark all eligible profiles as inactive
php artisan profiles:mark-inactive
```

**Output Example:**
```
Checking profiles for inactivity...
[User] Marked inactive: John Doe (1095 days)
[User] Marked inactive: Jane Smith (1200 days)
[Organization] Marked inactive: Community Center (800 days)
Processing complete: 3 profiles marked as inactive
```

**Log File:** `storage/logs/mark-inactive-profiles.log`
```
[2025-11-14 01:30:15] === Starting profile inactivity check ===
[2025-11-14 01:30:16] [User] Marked INACTIVE: John Doe (ID: 123) - Not logged in for 1095 days (last login: 2022-01-01)
[2025-11-14 01:30:17] [User] Marked INACTIVE: Jane Smith (ID: 456) - Not logged in for 1200 days (last login: 2021-09-15)
[2025-11-14 01:30:18] === Completed: 3 profiles marked inactive ===
```

**Schedule:** Runs daily at 01:30 (via `app/Console/Kernel.php`)

---

### 2. Process Inactive Profiles

**Command:** `php artisan profiles:process-inactive`

**Purpose:** Send warning emails and delete profiles based on inactivity duration.

**What It Does:**
1. Finds all User and Organization profiles where:
   - `inactive_at` is set (marked as inactive)
   - `deleted_at` is NULL (not already deleted)
2. Calculates days since `inactive_at`
3. Determines action based on thresholds:
   - Send Warning 1 email
   - Send Warning 2 email
   - Send Final Warning email
   - Delete profile
4. Emails are queued (not sent immediately)
5. Logs all actions to `storage/logs/inactive-profiles.log`

**Usage:**
```bash
# Process all inactive profiles
php artisan profiles:process-inactive
```

**Output Example:**
```
Processing inactive profiles...
[User] warning_1: John Doe (7 days remaining)
[User] warning_2: Jane Smith (5 days remaining)
[Organization] final: Community Center (1 day remaining)
[User] Deleted: Old User
Processing complete: 3 warnings sent, 1 profiles deleted
```

**Log File:** `storage/logs/inactive-profiles.log`
```
[2025-11-14 02:00:15] === Starting inactive profile processing ===
[2025-11-14 02:00:16] [User] warning_1 sent to John Doe (ID: 123) - Inactive for 7.5 days, 7.5 days remaining
[2025-11-14 02:00:17] [User] warning_2 sent to Jane Smith (ID: 456) - Inactive for 10.2 days, 4.8 days remaining
[2025-11-14 02:00:18] [Organization] final sent to Community Center (ID: 789) - Inactive for 14.1 days, 0.9 days remaining
[2025-11-14 02:00:19] [User] DELETED Old User (ID: 321) - Inactive for 15.5 days - Confirmation email sent to olduser@example.com
[2025-11-14 02:00:20] === Completed: 3 warnings, 1 deletions ===
```

**Schedule:** Runs daily at 02:00 (via `app/Console/Kernel.php`)

**Important Notes:**
- Emails are queued, not sent immediately (requires queue worker)
- Profiles with negative balances are skipped and logged
- Each profile receives only ONE email per day (based on current threshold)
- Soft-deleted profiles are automatically excluded
- Deletion confirmation emails are sent automatically after successful deletion

---

## Workflow Process

### Full Lifecycle Example

#### Timeline: Production Configuration

```
Day 0 (Jan 1, 2024):
├─ User logs in for last time
└─ last_login_at = 2024-01-01 10:00:00

Day 350 (Dec 16, 2024) at 01:30:
├─ profiles:mark-inactive runs
├─ Finds user (350 days since last login)
├─ Sets inactive_at = 2024-12-16 01:30:00
├─ Profile becomes hidden/labeled
└─ Log: "Marked INACTIVE: User (350 days)"

Day 350 (Dec 16, 2024) at 02:00:
├─ profiles:process-inactive runs
├─ User inactive for 0 days (just marked)
└─ No action (threshold not reached)

Day 357 (Dec 23, 2024) at 02:00:
├─ profiles:process-inactive runs
├─ User inactive for 7 days
├─ Threshold: warning_1 (7 days)
├─ Queues Warning 1 email
└─ Log: "warning_1 sent, 8 days remaining"

Email Content (Warning 1):
├─ Subject: "Warning: Your profile will be deleted soon"
├─ Header: "Inactive profile warning"
├─ Message: "Your profile has been inactive for 357 days..."
├─ Shows: Account balances, time remaining
└─ Button: "Activate profile"

Day 360 (Dec 26, 2024) at 02:00:
├─ profiles:process-inactive runs
├─ User inactive for 10 days
├─ Threshold: warning_2 (10 days)
├─ Queues Warning 2 email
└─ Log: "warning_2 sent, 5 days remaining"

Day 364 (Dec 30, 2024) at 02:00:
├─ profiles:process-inactive runs
├─ User inactive for 14 days
├─ Threshold: warning_final (14 days)
├─ Queues Final Warning email
└─ Log: "final sent, 1 day remaining"

Day 365 (Dec 31, 2024) at 02:00:
├─ profiles:process-inactive runs
├─ User inactive for 15 days
├─ Threshold: run_delete (15 days)
├─ Checks for negative balances → None
├─ Executes DeleteUser action (soft delete)
├─ Sets deleted_at = 2024-12-31 02:00:00
├─ Balance handling preferences stored in comment field
├─ Profile enters grace period (30 days by default)
├─ User can restore profile during grace period
├─ Deletion confirmation email sent
└─ Log: "DELETED User (365 days total)"

Day 395 (Jan 30, 2025) at 02:30:
├─ profiles:permanently-delete-expired runs
├─ Grace period expired (30 days since deletion)
├─ Executes permanent deletion
├─ Personal data anonymized
├─ Transactions anonymized
├─ Account balances handled per stored preferences
└─ Profile cannot be restored after this point
```

### User Reactivation Scenarios

#### Scenario 1: User Logs In During Warning Period
```
Day 357: Warning 1 sent
Day 358: User logs in
├─ Auto-reactivation enabled (re-activate_at_login = true)
├─ Sets inactive_at = NULL
├─ Profile becomes active again
├─ User sees "Welcome back!" message
└─ Process stops (profile no longer inactive)
```

#### Scenario 2: User Clicks "Activate Profile" Button
```
Day 359: User clicks button in warning email
├─ Redirected to login page with intended route
├─ User logs in successfully
├─ Auto-reactivation triggers
├─ Sets inactive_at = NULL
└─ Profile active, deletion prevented
```

#### Scenario 3: User Does Nothing
```
Day 357: Warning 1 sent → User ignores
Day 360: Warning 2 sent → User ignores
Day 364: Final Warning → User ignores
Day 365: Profile soft-deleted → Grace period begins (30 days)
Day 395: Grace period expires → Profile permanently deleted
```

#### Scenario 4: Profile Restoration During Grace Period
```
Day 365: Profile soft-deleted (automatic deletion after inactivity)
├─ Deletion confirmation email sent
├─ Profile enters 30-day grace period
└─ Profile can be restored by admin

Day 370: Admin restores profile (5 days after deletion)
├─ Command: php artisan profiles:restore username
├─ Sets deleted_at = NULL
├─ Clears stored balance handling preferences
├─ Profile becomes active again
└─ User can log in normally
```

---

## Scheduled Execution

### Scheduler Configuration

**Location:** `app/Console/Kernel.php` (lines 51-63)

```php
// Mark profiles as inactive when they haven't logged in for configured days
$schedule->command('profiles:mark-inactive')
    ->daily()
    ->at('01:30')
    ->withoutOverlapping()
    ->appendOutputTo(storage_path('logs/mark-inactive-profiles.log'));

// Process inactive profiles daily (send warnings and soft-delete profiles)
$schedule->command('profiles:process-inactive')
    ->daily()
    ->at('02:00')
    ->withoutOverlapping()
    ->appendOutputTo(storage_path('logs/inactive-profiles.log'));

// Permanently delete profiles after grace period expires
$schedule->command('profiles:permanently-delete-expired')
    ->daily()
    ->at('02:30')
    ->withoutOverlapping()
    ->appendOutputTo(storage_path('logs/permanent-deletions.log'));
```

### Execution Times

- **01:30**: Mark inactive profiles (Step 1)
- **02:00**: Process inactive profiles (Step 2 - soft delete)
- **02:30**: Permanently delete expired profiles (after grace period)

30-minute gap ensures profiles marked at 01:30 won't be immediately processed.

### Enabling the Scheduler

**Crontab Entry:**
```bash
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
```

**Verify Schedule:**
```bash
# List all scheduled tasks
php artisan schedule:list

# Test scheduler without waiting
php artisan schedule:test
```

---

## Testing

### Testing Configuration

For rapid testing, use minute-based values:

```php
'profile_inactive' => [
    'days_not_logged_in' => 0.000694,  // ~1 minute
],

'delete_profile' => [
    'days_after_inactive' => [
        'warning_1' => 0.001389,      // ~2 minutes after marked inactive
        'warning_2' => 0.00208,       // ~3 minutes after marked inactive
        'warning_final' => 0.00278,   // ~4 minutes after marked inactive
        'run_delete' => 0.00347,      // ~5 minutes after marked inactive
    ],
],
```

**Conversion Formula:** `days = minutes / 1440`

### Test Script: All 3 Warnings

**File:** `test-all-warnings.sh`

```bash
#!/bin/bash

USER_ID=${1:-102}

echo "=== Testing All 3 Warning Emails for User ID: $USER_ID ==="

# Warning 1: Set inactive_at to 2 minutes ago
echo "=== Test 1: Warning 1 (inactive for 2 minutes) ==="
php artisan tinker --execute="
\$user = App\Models\User::find($USER_ID);
\$user->inactive_at = now()->subMinutes(2);
\$user->save();
exit;
"
php artisan profiles:process-inactive
sleep 2

# Warning 2: Set inactive_at to 3 minutes ago
echo "=== Test 2: Warning 2 (inactive for 3 minutes) ==="
php artisan tinker --execute="
\$user = App\Models\User::find($USER_ID);
\$user->inactive_at = now()->subMinutes(3);
\$user->save();
exit;
"
php artisan profiles:process-inactive
sleep 2

# Warning Final: Set inactive_at to 4 minutes ago
echo "=== Test 3: Warning Final (inactive for 4 minutes) ==="
php artisan tinker --execute="
\$user = App\Models\User::find($USER_ID);
\$user->inactive_at = now()->subMinutes(4);
\$user->save();
exit;
"
php artisan profiles:process-inactive

echo "=== All 3 warning emails sent! ==="
php artisan queue:work --stop-when-empty --timeout=30
```

**Usage:**
```bash
chmod +x test-all-warnings.sh
./test-all-warnings.sh 102
```

### Manual Testing Steps

#### Test 1: Mark Inactive
```bash
# Setup test user
php artisan tinker --execute="
\$user = App\Models\User::find(102);
\$user->last_login_at = now()->subMinutes(2);
\$user->inactive_at = null;
\$user->save();
echo 'User setup: last login 2 minutes ago';
exit;
"

# Run mark-inactive command
php artisan profiles:mark-inactive

# Verify user is now inactive
php artisan tinker --execute="
\$user = App\Models\User::find(102);
echo 'inactive_at: ' . \$user->inactive_at . PHP_EOL;
echo 'Is active: ' . (\$user->isActive() ? 'Yes' : 'No');
exit;
"
```

#### Test 2: Warning Emails
```bash
# Setup user for Warning 1
php artisan tinker --execute="
\$user = App\Models\User::find(102);
\$user->inactive_at = now()->subMinutes(2);
\$user->save();
exit;
"

# Process inactive profiles
php artisan profiles:process-inactive

# Process email queue
php artisan queue:work --stop-when-empty

# Check Mailpit for email
```

#### Test 3: Deletion
```bash
# Setup user for deletion
php artisan tinker --execute="
\$user = App\Models\User::find(102);
\$user->inactive_at = now()->subMinutes(6);
\$user->save();
exit;
"

# Process inactive profiles (will delete)
php artisan profiles:process-inactive

# Verify deletion
php artisan tinker --execute="
\$user = App\Models\User::withTrashed()->find(102);
echo 'deleted_at: ' . \$user->deleted_at . PHP_EOL;
exit;
"
```

### Verification Checklist

- [ ] Command logs to correct file
- [ ] Profiles marked inactive at right threshold
- [ ] Warning 1 email received
- [ ] Warning 2 email received
- [ ] Final warning email received
- [ ] Profile deleted at correct threshold
- [ ] Profile with negative balance skipped
- [ ] Already deleted profiles not reprocessed
- [ ] Email queue processed successfully
- [ ] Transactions anonymized after deletion

---

## Production Configuration

### Recommended Production Settings

```php
'profile_inactive' => [
    'days_not_logged_in' => 350,     // ~11.5 months
    're-activate_at_login' => true,
    'messenger_hidden' => true,
    'profile_search_hidden' => true,
    'profile_hidden' => true,
    'profile_labeled' => true,
],

'delete_profile' => [
    'days_after_inactive' => [
        'warning_1' => 7,      // 357 days total (~11.75 months)
        'warning_2' => 10,     // 360 days total (~11.83 months)
        'warning_final' => 14, // 364 days total (~12 months)
        'run_delete' => 15,    // 365 days total (1 year)
    ],
],
```

**Total Timeline:** 1 year from last login to deletion

### Alternative: Strict 3-Year Retention

```php
'profile_inactive' => [
    'days_not_logged_in' => 1080,  // ~3 years
],

'delete_profile' => [
    'days_after_inactive' => [
        'warning_1' => 7,      // 1087 days total
        'warning_2' => 10,     // 1090 days total
        'warning_final' => 14, // 1094 days total
        'run_delete' => 15,    // 1095 days total (3 years)
    ],
],
```

### Balance Handling Options

**Option 1: Donate to Organization (Recommended)**
```php
'account_balances' => [
    'donate_balances_to_organization_account_specified' => true,
    'transfer_balances_to_bank_client' => false,
    'transfer_balances_to_account_id' => null,
    'transfer_balances_to_debit_account' => true,
],
```
Users can select an organization to receive their balance during deletion flow.

**Option 2: Remove Currency (Debit Account)**
```php
'account_balances' => [
    'donate_balances_to_organization_account_specified' => false,
    'transfer_balances_to_bank_client' => false,
    'transfer_balances_to_account_id' => null,
    'transfer_balances_to_debit_account' => true,  // Removes from circulation
],
```
Balance transferred to debit account (removes dead currency).

**Option 3: Transfer to Specific Account**
```php
'account_balances' => [
    'donate_balances_to_organization_account_specified' => false,
    'transfer_balances_to_bank_client' => false,
    'transfer_balances_to_account_id' => 123,  // Specific account ID
    'transfer_balances_to_debit_account' => false,
],
```
All balances go to a designated account (e.g., community fund).

---

## Troubleshooting

### Issue: Commands Not Running Automatically

**Symptoms:**
- Profiles not being marked inactive
- No warning emails sent
- Logs not updating

**Diagnosis:**
```bash
# Check if scheduler is configured
crontab -l | grep artisan

# Verify schedule:run works
php artisan schedule:run

# List scheduled tasks
php artisan schedule:list
```

**Solution:**
```bash
# Add to crontab
crontab -e

# Add this line:
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1

# Verify
php artisan schedule:test
```

---

### Issue: Emails Not Sending

**Symptoms:**
- Log shows "warning sent" but no email received
- Queue contains jobs but they're not processing

**Diagnosis:**
```bash
# Check queue status
php artisan queue:failed

# Check mail configuration
php artisan tinker --execute="dd(config('mail'));"

# Verify queue worker is running
ps aux | grep "queue:work"
```

**Solution:**
```bash
# Start queue worker
php artisan queue:work

# Or use supervisor for persistent queue worker
# See: Laravel Queue documentation
```

---

### Issue: Already Deleted Profiles Being Processed

**Symptoms:**
- Log shows deletions for profiles that were already deleted
- "DELETED" entries for same profile multiple times

**Fixed In:** Version with `whereNull('deleted_at')` filter (app/Console/Commands/ProcessInactiveProfiles.php:50, 61)

**Verification:**
```bash
# Check ProcessInactiveProfiles.php contains:
grep "whereNull('deleted_at')" app/Console/Commands/ProcessInactiveProfiles.php

# Should see:
# ->whereNull('deleted_at') // Exclude already deleted profiles
```

---

### Issue: Profiles with Negative Balances Not Skipped

**Symptoms:**
- Error when trying to delete profile with negative balance
- Deletion process crashes

**Expected Behavior:**
- Profiles with negative balances should be skipped
- Log entry: "SKIPPED deletion - Has negative balance"

**Verification:**
```bash
# Check logs for skip messages
grep "SKIPPED" storage/logs/inactive-profiles.log

# Manually test
php artisan tinker --execute="
\$user = App\Models\User::find(102);
\$accounts = \$user->accounts()->active()->notRemoved()->get();
foreach (\$accounts as \$account) {
    echo \$account->name . ': ' . \$account->balance . PHP_EOL;
}
exit;
"
```

---

### Issue: Wrong Warning Level Sent

**Symptoms:**
- User receives Warning 2 but should get Warning 1
- Only one warning email instead of three

**Understanding:**
The system sends ONE warning per day based on CURRENT inactive duration:
- If user is 7 days inactive → Warning 1
- If user is 10 days inactive → Warning 2
- If user is 14 days inactive → Final Warning

This is by design. Users naturally progress through warnings as time passes.

**Not a Bug:** Each profile gets the appropriate warning for their current state, not all warnings at once.

---

### Issue: Configuration Not Taking Effect

**Symptoms:**
- Changed config values but behavior unchanged
- Still using old thresholds

**Solution:**
```bash
# Clear config cache
php artisan config:clear

# Rebuild cache
php artisan config:cache

# Verify config loaded
php artisan tinker --execute="
echo 'days_not_logged_in: ' . timebank_config('profile_inactive.days_not_logged_in') . PHP_EOL;
echo 'warning_1: ' . timebank_config('delete_profile.days_after_inactive.warning_1') . PHP_EOL;
exit;
"
```

---

## Related Systems

### 1. Profile Inactive System

**Documentation:** [PROFILE_INACTIVE_CONFIG.md](PROFILE_INACTIVE_CONFIG.md)

The auto-delete system builds on the profile inactive system:
- Uses same `inactive_at` field
- Respects same visibility/hiding rules
- Triggered by same `isActive()` method

**Integration:**
- `MarkInactiveProfiles` sets `inactive_at`
- Profile becomes hidden (per inactive config)
- `ProcessInactiveProfiles` processes these inactive profiles
- Progressive warnings sent before deletion

### 2. User Deletion Action

**Location:** `app/Actions/Jetstream/DeleteUser.php`

The auto-delete system uses the existing `DeleteUser` action:
```php
$deleteUser = new DeleteUser();
$deleteUser->delete($profile, 'delete', null);
```

**What It Does:**
- Anonymizes profile data
- Preserves transactions (anonymized)
- Handles account balances (per config)
- Soft deletes profile
- **Note:** Does NOT send deletion email (handled by ProcessInactiveProfiles command)

### 3. Email System

#### Warning Emails

**Mailable Classes:**
- `app/Mail/InactiveProfileWarning1Mail.php`
- `app/Mail/InactiveProfileWarning2Mail.php`
- `app/Mail/InactiveProfileWarningFinalMail.php`

**Email Templates:**
- `resources/views/emails/inactive-profiles/{locale}/warning-1.blade.php`
- `resources/views/emails/inactive-profiles/{locale}/warning-2.blade.php`
- `resources/views/emails/inactive-profiles/{locale}/warning-final.blade.php`

**Languages Supported:** en, nl, de, es, fr

#### Deletion Confirmation Email

**Mailable Class:**
- `app/Mail/UserDeletedMail.php`

**Email Template:**
- `resources/views/emails/administration/{locale}/user-deleted.blade.php`

**Sent by:** `ProcessInactiveProfiles` command (app/Console/Commands/ProcessInactiveProfiles.php:194)

**When:** Immediately after successful profile deletion

**Content:**
- Account details (username, full name, email)
- Deletion timestamp
- Balance handling information (total balance, donation details if applicable)
- Confirmation that data has been permanently deleted and anonymized
- Note that action is irreversible

**Variables:**
```php
[
    'time' => '18 November 2025, 22:26',
    'deletedUser' => (object)[
        'name' => 'ProfileName',
        'full_name' => 'Full Profile Name',
        'lang_preference' => 'en',
    ],
    'mail' => 'user@example.com',
    'balanceHandlingOption' => 'delete',
    'totalBalance' => 120, // in minutes
    'donationAccountId' => null,
    'donationAccountName' => null,
    'donationOrganizationName' => null,
]
```

**Languages Supported:** en, nl, de, es, fr

### 4. Queue System

All emails (warnings and deletion confirmations) are queued for asynchronous delivery:
- Uses Laravel queue system
- Requires queue worker running
- Prevents command timeout for large batches
- Emails are sent in the user's preferred language (`lang_preference`)

**Queue Configuration:** `config/queue.php`

**Email Types Queued:**
1. Warning emails (InactiveProfileWarning1Mail, InactiveProfileWarning2Mail, InactiveProfileWarningFinalMail)
2. Deletion confirmation emails (UserDeletedMail)

### 5. Profile Deletion Grace Period

**Documentation:** [PROFILE_DELETION_GRACE_PERIOD.md](PROFILE_DELETION_GRACE_PERIOD.md)

When profiles are deleted (either automatically or manually), they enter a grace period before permanent deletion:

**How It Works:**
- Profile is soft-deleted (`deleted_at` timestamp set)
- Balance handling preferences stored in `comment` field as JSON
- Profile remains in grace period for configurable days (default: 30)
- Profile can be restored during grace period using `php artisan profiles:restore`
- After grace period expires, `profiles:permanently-delete-expired` command:
  - Anonymizes profile data permanently
  - Handles account balances per stored preferences
  - Makes restoration impossible

**Configuration:**
```php
'delete_profile' => [
    'grace_period_days' => 30,  // Days before permanent deletion
],
```

**Restoration Command:**
```bash
# List all restorable profiles
php artisan profiles:restore --list

# Restore specific profile
php artisan profiles:restore username
```

**Integration with Auto-Delete:**
- Auto-deleted profiles enter grace period automatically
- Deletion confirmation email mentions grace period
- Admin can restore accidentally deleted profiles
- Permanent deletion happens 30 days after initial deletion

---

## Monitoring

### Log Files

**Mark Inactive Log:** `storage/logs/mark-inactive-profiles.log`
```bash
# View recent marks
tail -n 50 storage/logs/mark-inactive-profiles.log

# Count marked today
grep "$(date +%Y-%m-%d)" storage/logs/mark-inactive-profiles.log | wc -l
```

**Process Inactive Log:** `storage/logs/inactive-profiles.log`
```bash
# View recent activity
tail -n 100 storage/logs/inactive-profiles.log

# Count deletions today
grep "DELETED" storage/logs/inactive-profiles.log | grep "$(date +%Y-%m-%d)" | wc -l

# Count warnings sent
grep "warning_1 sent" storage/logs/inactive-profiles.log | grep "$(date +%Y-%m-%d)" | wc -l
```

### Metrics to Track

**Daily:**
- Number of profiles marked inactive
- Number of warnings sent (by level)
- Number of profiles deleted
- Number of profiles skipped (negative balance)

**Weekly:**
- Total inactive profiles
- Inactive profiles by stage (warning 1, 2, final, pending deletion)
- Average time to deletion
- Reactivation rate (users who logged back in)

**Monthly:**
- Data retention compliance
- Account balance disposal (by method)
- User notification effectiveness
- Error rates

---

## Best Practices

### 1. Communication Strategy

**Before Implementation:**
- Announce new auto-delete policy to all users
- Update Terms of Service
- Add FAQ explaining the process
- Give 60-day grace period before first deletions

**During Operation:**
- Clear warning emails with specific dates
- Multiple warnings with escalating urgency
- Easy reactivation process (just login)
- Contact information for questions

### 2. Data Protection

**GDPR Compliance:**
- Auto-delete supports "right to be forgotten"
- Users can delete account anytime (no waiting)
- Clear retention policy stated upfront
- Audit trail via log files

**Balance Protection:**
- Never delete profiles with negative balances
- Allow balance donation before deletion
- Clear communication about balance disposal
- Option to withdraw balance before inactivity

### 3. Testing Strategy

**Before Production:**
- [ ] Test full cycle with minute-based config
- [ ] Verify all 3 warning emails send correctly
- [ ] Test deletion with various balance states
- [ ] Confirm transactions anonymize properly
- [ ] Verify scheduler runs correctly
- [ ] Test queue worker processes emails
- [ ] Check logs for errors
- [ ] Test reactivation flow

**In Production:**
- [ ] Monitor logs daily for first month
- [ ] Review deletion counts weekly
- [ ] Track reactivation rates
- [ ] Collect user feedback on warnings
- [ ] Adjust timing if needed

### 4. Gradual Rollout

**Phase 1: Warnings Only (Month 1)**
```php
'days_after_inactive' => [
    'warning_1' => 7,
    'warning_2' => 10,
    'warning_final' => 14,
    'run_delete' => 99999,  // Never delete (testing)
],
```
Send warnings but don't delete. Monitor feedback.

**Phase 2: Long Timeline (Month 2-3)**
```php
'days_after_inactive' => [
    'warning_1' => 30,
    'warning_2' => 45,
    'warning_final' => 60,
    'run_delete' => 90,  // 3 months from marked inactive
],
```
Very generous timeline while users adjust.

**Phase 3: Normal Timeline (Month 4+)**
```php
'days_after_inactive' => [
    'warning_1' => 7,
    'warning_2' => 10,
    'warning_final' => 14,
    'run_delete' => 15,
],
```
Standard production timeline.

---

## See Also

- [PROFILE_DELETION_GRACE_PERIOD.md](PROFILE_DELETION_GRACE_PERIOD.md) - Grace period and profile restoration
- [PROFILE_INACTIVE_CONFIG.md](PROFILE_INACTIVE_CONFIG.md) - Profile inactive system documentation
- [EMAIL-TESTING-GUIDE.md](../EMAIL-TESTING-GUIDE.md) - Email testing procedures
- `app/Console/Commands/MarkInactiveProfiles.php` - Mark inactive command
- `app/Console/Commands/ProcessInactiveProfiles.php` - Process inactive command
- `app/Console/Commands/PermanentlyDeleteExpiredProfiles.php` - Permanent deletion command
- `app/Console/Commands/RestoreDeletedProfile.php` - Profile restoration command
- `app/Actions/Jetstream/DeleteUser.php` - User deletion action
- `app/Actions/Jetstream/RestoreProfile.php` - Profile restoration action
- `app/Mail/UserDeletedMail.php` - Deletion confirmation email mailable
- `app/Http/Livewire/Profile/DeleteUserForm.php` - Manual deletion form (uses same email)
- `resources/views/emails/administration/{locale}/user-deleted.blade.php` - Deletion email template
- `config/timebank-default.php` - Full configuration reference

---

## Changelog

### 2025-12-19 - Grace Period Implementation
- Added 30-day grace period for deleted profiles
- Profiles now soft-deleted first, permanently deleted after grace period
- Created `profiles:permanently-delete-expired` scheduled command
- Created `profiles:restore` command for profile restoration
- Balance handling deferred until permanent deletion
- Updated deletion emails to mention grace period
- Added comprehensive grace period documentation

### 2025-11-18 - Deletion Confirmation Emails
- Added automatic deletion confirmation emails
- Integrated `UserDeletedMail` into `ProcessInactiveProfiles` command
- Email sent immediately after successful profile deletion
- Updated log messages to include "Confirmation email sent to {email}"
- All deletion emails queued for asynchronous delivery

### 2025-11-14 - Initial Implementation
- Created two-step auto-delete system
- Implemented `MarkInactiveProfiles` command
- Refactored `ProcessInactiveProfiles` to use `inactive_at`
- Added scheduler configuration
- Created comprehensive documentation
- Fixed soft-delete exclusion bug
- Added production and testing configurations

---

**Last Updated:** 2025-12-19
**Maintainer:** Development Team
**Status:** PRODUCTION READY
**Commands:** `profiles:mark-inactive`, `profiles:process-inactive`, `profiles:permanently-delete-expired`, `profiles:restore`
