# Process Triggers Script

## Overview

The `process-triggers.php` script is responsible for processing seller marketing triggers and queuing messages for eligible customers. This is part of a two-script system:

1. **process-triggers.php** - Finds customers matching trigger criteria and queues messages (this script)
2. **process-sms-queue.php** - Sends the queued messages via SMS

## Purpose

This script does **NOT** send messages directly. Instead, it:
- Evaluates trigger conditions (days since event, birthdays, etc.)
- Finds customers matching those conditions
- Queues personalized messages in the central queue
- Updates trigger processing timestamps

The actual message sending is handled by `process-sms-queue.php`.

## Command Line Options

### Basic Options

| Option | Description | Default |
|--------|-------------|---------|
| `--store=ou00` | Process specific store only | All stores |
| `--all-stores` | Process all active stores | Yes |
| `--trigger-id=123` | Process specific trigger only | All triggers |
| `--dry-run` | Don't queue messages, just report | Disabled |
| `--verbose` | Show detailed progress output | Disabled |
| `--help` | Show help message | - |

## Usage Examples

### Process All Stores (Production)
```bash
php tasker/process-triggers.php --all-stores
```

### Test Specific Store (Dry Run)
```bash
php tasker/process-triggers.php --store=ou00 --dry-run --verbose
```
This will show what would happen without actually queuing any messages.

### Process Specific Trigger
```bash
php tasker/process-triggers.php --store=ou00 --trigger-id=5
```
Useful for testing a newly created trigger.

### Debug All Stores with Verbose Output
```bash
php tasker/process-triggers.php --all-stores --verbose
```
Shows detailed progress for all stores and triggers.

## How It Works

### Workflow

```
1. Check lock file (prevent concurrent runs)
2. Parse command line arguments
3. Setup logging to logs/process-triggers-{date}.log
4. Get stores to process
   - Single store if --store specified
   - All active stores if --all-stores or no options
5. For each store:
   a. Connect to store database
   b. Get active triggers (filtered by --trigger-id if specified)
   c. For each trigger:
      - Check if should run (last_processed time)
      - Check active_days configuration
      - Find matching customers
      - Personalize messages
      - Queue messages (unless --dry-run)
      - Update last_processed timestamp
6. Output summary statistics
7. Remove lock file
```

### Trigger Processing Rules

The script only processes triggers that:
- Have status = 'active'
- Haven't expired (expire_date is null or in future)
- Haven't been processed in the last hour (unless --trigger-id specified)
- Match current day of week (if active_days configured)

### Customer Matching

Different trigger types use different customer matching logic:

#### Days Since Event
Matches customers where specific event occurred exactly N days ago:
- `last_visit` - Last store visit
- `last_purchase` - Last item purchased
- `last_sold` - Last item sold to store
- `signup` - Customer signup date

#### Days Since Sold
Matches customers who sold items exactly N days ago.

#### Birthday
Matches customers whose birthday is today (or N days offset).

#### Expiring Points
Matches customers with loyalty points expiring in N days.

#### Custom
Flexible matching based on custom criteria configuration.

### Message Personalization

Messages support these variables:
- `%customer%` or `%firstname%` - Customer first name
- `%lastname%` - Customer last name
- `%company%` - Store company name (Plato's Closet, etc.)
- `%store%` - Store city
- `%coop%` - Same as %company%
- `%points%` - Customer loyalty points

Example:
```
"Hi %firstname%, it's been 30 days since your last visit to %company% %store%.
We'd love to see you again!"
```

## Safety Features

### Lock File
- Location: `/tmp/process-triggers.lock`
- Prevents concurrent runs
- Valid for 1 hour
- Automatically removed on exit

### Signal Handling
Gracefully handles:
- SIGTERM (kill command)
- SIGINT (Ctrl+C)
- Ensures lock file is cleaned up

### Error Handling
- Per-store error handling (one store failure doesn't stop others)
- Per-trigger error handling
- Per-customer error handling
- All errors logged to file and summary

### Limits
- Maximum 500 customers per trigger (safety limit)
- Only processes active stores
- Respects opt-in status (optInText = 1)
- Filters out invalid phone numbers

## Logging

### Console Output
Standard output shows:
- Start/completion messages
- Per-store summaries
- Final statistics
- Error counts

### File Logging
Logs written to: `logs/process-triggers-{date}.log`

Log entries include:
- Timestamp
- Store completion status
- Trigger statistics
- Error details

Example log entry:
```
[2025-11-24 12:00:00] Starting trigger processor...
[2025-11-24 12:00:15] Store ou00 completed: 3 triggers, 45 messages queued
[2025-11-24 12:00:30] Trigger processing completed: 5 stores, 15 triggers, 150 messages queued, 0 errors
```

## Cron Setup

### Recommended Schedule

Run hourly to process all triggers:
```cron
0 * * * * /usr/bin/php /path/to/buyerkiosk-web/tasker/process-triggers.php >> /path/to/logs/process-triggers.log 2>&1
```

### Alternative Schedules

Every 4 hours:
```cron
0 */4 * * * /usr/bin/php /path/to/buyerkiosk-web/tasker/process-triggers.php --all-stores
```

Daily at 9 AM:
```cron
0 9 * * * /usr/bin/php /path/to/buyerkiosk-web/tasker/process-triggers.php --all-stores --verbose
```

## Dry Run Mode

The `--dry-run` flag is extremely useful for testing:

### What It Does
- Finds matching customers
- Shows what would be queued
- Does NOT actually queue messages
- Does NOT update last_processed timestamps
- Shows detailed output with --verbose

### Use Cases
1. **Testing New Triggers**
   ```bash
   php tasker/process-triggers.php --store=ou00 --trigger-id=5 --dry-run --verbose
   ```

2. **Checking Customer Counts Before Full Run**
   ```bash
   php tasker/process-triggers.php --all-stores --dry-run
   ```

3. **Debugging Trigger Configuration**
   ```bash
   php tasker/process-triggers.php --store=ou00 --dry-run --verbose
   ```

## Verbose Mode

Add `--verbose` to see detailed progress:

### Without Verbose
```
[2025-11-24 12:00:00] Starting trigger processor...
=================================================
[2025-11-24 12:00:30] Trigger processing completed
=================================================
Stores processed:    5
Triggers processed:  15
Customers matched:   150
Messages queued:     150
Errors:              0
=================================================
```

### With Verbose
```
[2025-11-24 12:00:00] Starting trigger processor...
Processing all active stores
Found 5 store(s) to process

=== Processing Store: ou00 ===
  Trigger #1: 30-Day Re-engagement
  Type: days_since_event
  Found 25 matching customer(s)
    Customer: John Doe (555-1234)
    Message: Hi John, it's been 30 days since your last visit...

Store Summary:
  - Triggers processed: 3
  - Customers matched: 45
  - Messages queued: 45

[... continues for other stores ...]
```

## Output Statistics

### Summary Report
The script always outputs a summary with these metrics:

- **Stores processed** - Number of stores successfully processed
- **Triggers processed** - Total triggers evaluated
- **Customers matched** - Total customers found matching criteria
- **Messages queued** - Total messages added to queue
- **Errors** - Count of any errors encountered

### Error Details
With `--verbose`, error details are shown:
```
Errors:              2

Error details:
  * Store ou00: Trigger 5 (Birthday Campaign): Message template not found
  * Store pa00: Database connection timeout
```

## Integration with Queue System

This script works in tandem with the queue processor:

### Message Flow
```
process-triggers.php
    ↓ (finds customers & queues messages)
seller_marketing_queue table
    ↓ (stores pending messages)
process-sms-queue.php
    ↓ (sends messages)
SMS Provider (Vonage/Twilio)
    ↓
Customer receives SMS
```

### Queue Tables

**seller_marketing_queue** (central database):
- Stores all queued messages
- Contains store_id, phone, message, etc.
- Processed by process-sms-queue.php

**seller_marketing_customer_log** (per-store database):
- Logs all messages sent to customers
- Tracks trigger_id and customer_id
- Used for reporting and analytics

## Troubleshooting

### Common Issues

#### Script Already Running
```
[2025-11-24 12:00:00] Process already running (lock file exists). Exiting.
```
**Solution:** Wait for previous run to complete, or remove stale lock file:
```bash
rm /tmp/process-triggers.lock
```

#### Store Not Found
```
Store ou99 not found or not active
```
**Solution:** Check store exists and is active in stores table.

#### Trigger Not Found
```
Trigger 99 not found or not active
```
**Solution:** Verify trigger exists and has status='active'.

#### No Customers Found
Common reasons:
1. Timing is off (e.g., birthday trigger on wrong day)
2. No customers match criteria
3. All matching customers have optInText = 0
4. Phone numbers missing or invalid

**Debug with:**
```bash
php tasker/process-triggers.php --store=ou00 --trigger-id=X --dry-run --verbose
```

#### Database Connection Errors
```
FATAL ERROR: SQLSTATE[HY000] [2002] Connection refused
```
**Solution:**
- Check database credentials in .env
- Verify MySQL is running
- Check network connectivity

### Debug Mode

For maximum debugging output:
```bash
php tasker/process-triggers.php --store=ou00 --trigger-id=5 --dry-run --verbose 2>&1 | tee debug.log
```

This will:
- Process single store and trigger
- Show all output
- Not queue messages
- Save output to debug.log

## Performance Considerations

### Processing Time
- Typical store: 2-5 seconds
- Large store with many triggers: 10-30 seconds
- All stores: 1-5 minutes (depends on number of active stores)

### Resource Usage
- Memory: ~50-100MB per process
- CPU: Minimal (mostly database queries)
- Database: Read-heavy, some writes for queue

### Optimization Tips
1. **Run during low-traffic hours** - Less database contention
2. **Process specific stores** - Instead of all stores at once
3. **Limit customer counts** - Use trigger config to set reasonable maximums
4. **Monitor queue growth** - Ensure process-sms-queue.php keeps up

## Best Practices

### Testing New Triggers
1. Create trigger with status='inactive'
2. Test configuration in database
3. Set status='active'
4. Run with dry-run first:
   ```bash
   php tasker/process-triggers.php --store=ou00 --trigger-id=X --dry-run --verbose
   ```
5. Review customer matches
6. Run without dry-run to actually queue
7. Monitor queue processor

### Production Deployment
1. Test in single store first
2. Monitor logs closely
3. Check queue processor is running
4. Verify messages are being sent
5. Roll out to all stores

### Monitoring
1. Set up cron email notifications for errors
2. Check logs regularly: `logs/process-triggers-*.log`
3. Monitor queue table size
4. Track message delivery rates
5. Watch for error patterns

## Advanced Usage

### Custom Frequency
Skip the hourly check by processing specific trigger:
```bash
php tasker/process-triggers.php --store=ou00 --trigger-id=5
```
This bypasses the "last processed within 1 hour" check.

### Parallel Processing
Process multiple stores in parallel (separate terminals):
```bash
# Terminal 1
php tasker/process-triggers.php --store=ou00 &

# Terminal 2
php tasker/process-triggers.php --store=pa00 &
```
Note: Lock file prevents same script from running twice, but different stores can run concurrently if needed.

### Manual Testing
Test customer matching without the full script:
```php
// Direct database query
SELECT customerID, firstName, lastName, phone
FROM customers
WHERE lastVisit IS NOT NULL
AND DATEDIFF(CURDATE(), lastVisit) = 30
AND phone IS NOT NULL AND optInText = 1;
```

## Related Scripts

- **process-sms-queue.php** - Sends queued messages
- **process-sms-triggers.php** - Legacy trigger processor (deprecated)

## Database Schema

### seller_marketing_triggers
```sql
CREATE TABLE seller_marketing_triggers (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(255),
  type VARCHAR(50),
  message_id INT,
  status ENUM('active','inactive','expired'),
  config JSON,
  last_processed DATETIME,
  expire_date DATE,
  active_days VARCHAR(50),  -- e.g., "1,2,3,4,5" for Mon-Fri
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```

### seller_marketing_queue
```sql
CREATE TABLE seller_marketing_queue (
  id INT PRIMARY KEY AUTO_INCREMENT,
  store_id INT,
  phone VARCHAR(20),
  message TEXT,
  message_type ENUM('trigger','blast'),
  trigger_id INT,
  customer_id INT,
  status ENUM('pending','sent','failed'),
  send_at DATETIME,
  sent_at DATETIME,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

## Support

For issues or questions:
1. Check logs: `logs/process-triggers-{date}.log`
2. Run with `--dry-run --verbose` to debug
3. Review this documentation
4. Check related code: `userfrosting/models/Class/SellerMarketing/`
