# External API Notification Service

## Overview

The PX360 notification system has been extended to support sending emails and SMS via external API endpoints. This allows integration with third-party notification services while maintaining backward compatibility with existing Django SMTP functionality.

## Features

- ✅ **Separate API Methods**: `send_email_via_api()` and `send_sms_via_api()`
- ✅ **Generic Request Format**: Simple JSON structure compatible with most APIs
- ✅ **Flexible Authentication**: Supports both `Bearer` token and `X-API-KEY` header
- ✅ **Fire-and-Forget**: Accepts any 2xx response (API processes in background)
- ✅ **Retry Logic**: Exponential backoff with configurable max retries
- ✅ **Comprehensive Logging**: All attempts logged to Django logger and NotificationLog
- ✅ **Error Handling**: Handles timeouts, connection errors, and HTTP errors
- ✅ **Configurable**: All settings via environment variables
- ✅ **Non-Breaking**: Existing code continues to work unchanged

## Configuration

### Environment Variables

Add these to your `.env` file:

```bash
# External Email API
EMAIL_API_ENABLED=false                    # Enable/disable email API
EMAIL_API_URL=https://api.example.com/send-email
EMAIL_API_KEY=your-api-key-here
EMAIL_API_AUTH_METHOD=bearer               # Options: bearer, api_key
EMAIL_API_METHOD=POST
EMAIL_API_TIMEOUT=10                       # Request timeout in seconds
EMAIL_API_MAX_RETRIES=3                    # Maximum retry attempts
EMAIL_API_RETRY_DELAY=2                    # Initial delay in seconds

# External SMS API
SMS_API_ENABLED=false                       # Enable/disable SMS API
SMS_API_URL=https://api.example.com/send-sms
SMS_API_KEY=your-api-key-here
SMS_API_AUTH_METHOD=bearer                 # Options: bearer, api_key
SMS_API_METHOD=POST
SMS_API_TIMEOUT=10                         # Request timeout in seconds
SMS_API_MAX_RETRIES=3                      # Maximum retry attempts
SMS_API_RETRY_DELAY=2                      # Initial delay in seconds
```

### Settings Structure

The configuration is automatically loaded into `settings.EXTERNAL_NOTIFICATION_API`:

```python
EXTERNAL_NOTIFICATION_API = {
    'email': {
        'enabled': env.bool('EMAIL_API_ENABLED', default=False),
        'url': env('EMAIL_API_URL', default=''),
        'api_key': env('EMAIL_API_KEY', default=''),
        'auth_method': env('EMAIL_API_AUTH_METHOD', default='bearer'),
        'method': env('EMAIL_API_METHOD', default='POST'),
        'timeout': env.int('EMAIL_API_TIMEOUT', default=10),
        'max_retries': env.int('EMAIL_API_MAX_RETRIES', default=3),
        'retry_delay': env.int('EMAIL_API_RETRY_DELAY', default=2),
    },
    'sms': {
        'enabled': env.bool('SMS_API_ENABLED', default=False),
        'url': env('SMS_API_URL', default=''),
        'api_key': env('SMS_API_KEY', default=''),
        'auth_method': env('SMS_API_AUTH_METHOD', default='bearer'),
        'method': env('SMS_API_METHOD', default='POST'),
        'timeout': env.int('SMS_API_TIMEOUT', default=10),
        'max_retries': env.int('SMS_API_MAX_RETRIES', default=3),
        'retry_delay': env.int('SMS_API_RETRY_DELAY', default=2),
    },
}
```

## API Request Format

### Email API Request

**Endpoint**: POST to `EMAIL_API_URL`

**Headers**:
```
Content-Type: application/json
Authorization: Bearer {EMAIL_API_KEY}  # or X-API-KEY: {EMAIL_API_KEY}
```

**Request Body**:
```json
{
  "to": "recipient@example.com",
  "subject": "Email Subject",
  "message": "Plain text message",
  "html_message": "<html>Optional HTML content</html>"
}
```

**Expected Response**: Any 2xx status code (200-299)

### SMS API Request

**Endpoint**: POST to `SMS_API_URL`

**Headers**:
```
Content-Type: application/json
Authorization: Bearer {SMS_API_KEY}  # or X-API-KEY: {SMS_API_KEY}
```

**Request Body**:
```json
{
  "to": "+966501234567",
  "message": "SMS message text"
}
```

**Expected Response**: Any 2xx status code (200-299)

## Usage

### Basic Email via API

```python
from apps.notifications.services import NotificationService

# Simple email
log = NotificationService.send_email_via_api(
    message='Your account has been created',
    email='user@example.com',
    subject='Welcome to PX360'
)

# Email with HTML
log = NotificationService.send_email_via_api(
    message='Plain text version',
    email='user@example.com',
    subject='Welcome',
    html_message='<h1>Welcome</h1><p>Your account is ready!</p>'
)

# Email with tracking
complaint = Complaint.objects.get(id=123)
log = NotificationService.send_email_via_api(
    message='You have a new complaint...',
    email='staff@example.com',
    subject='New Complaint',
    related_object=complaint,
    metadata={'complaint_id': str(complaint.id)}
)
```

### Basic SMS via API

```python
from apps.notifications.services import NotificationService

# Simple SMS
log = NotificationService.send_sms_via_api(
    message='Your verification code is 123456',
    phone='+966501234567'
)

# SMS with tracking
survey_instance = SurveyInstance.objects.get(id=123)
log = NotificationService.send_sms_via_api(
    message='Please complete your survey: https://...',
    phone='+966501234567',
    related_object=survey_instance,
    metadata={'survey_id': str(survey_instance.id)}
)
```

### Comparison with Existing Methods

```python
from apps.notifications.services import NotificationService

# Existing method - uses Django SMTP
NotificationService.send_email(
    email='user@example.com',
    subject='Welcome',
    message='Hello!'
)

# New method - uses external API
NotificationService.send_email_via_api(
    message='Hello!',
    email='user@example.com',
    subject='Welcome'
)
```

## Retry Logic

Both API methods implement exponential backoff retry:

1. **Initial attempt** (retry 0)
2. Wait 2 seconds (retry_delay)
3. **First retry** (retry 1)
4. Wait 4 seconds (retry_delay × 2^1)
5. **Second retry** (retry 2)
6. Wait 8 seconds (retry_delay × 2^2)
7. **Final retry** (retry 3)

Configurable via:
- `EMAIL_API_MAX_RETRIES` / `SMS_API_MAX_RETRIES` (default: 3)
- `EMAIL_API_RETRY_DELAY` / `SMS_API_RETRY_DELAY` (default: 2 seconds)

## Error Handling

All errors are handled gracefully:

### Connection Errors
- Logged as "Connection error"
- Retry attempted if retries available

### Timeout Errors
- Logged as "Request timeout"
- Retry attempted if retries available

### HTTP Errors
- Non-2xx status codes logged with status number
- Retry attempted if retries available

### Unexpected Errors
- Full error message logged
- Retry attempted if retries available

### Final State
- After all retries exhausted, NotificationLog marked as `failed`
- Error details stored in log's error_message field

## Database Tracking

All API notifications are tracked in the `NotificationLog` model:

```python
log = NotificationService.send_email_via_api(
    message='Hello',
    email='user@example.com',
    subject='Test'
)

# Check status
print(log.status)  # 'sent' or 'failed'

# Check metadata
print(log.metadata)
# {
#     'api_url': 'https://api.example.com/send-email',
#     'auth_method': 'bearer',
#     ...
# }
```

## Logging

All API calls are logged to Django logger at different levels:

### INFO Level
- Successful API calls
- API request initiation

### WARNING Level
- Non-2xx HTTP responses
- Connection errors (during retries)
- Timeout errors (during retries)

### ERROR Level
- Unexpected exceptions
- Final failure after all retries

Logs are written to:
- Console (in development)
- `logs/px360.log` (file)
- `logs/integrations.log` (integration-specific)

## Examples

### Example 1: Send Welcome Email via API

```python
def send_welcome_email(user):
    from apps.notifications.services import NotificationService
    
    log = NotificationService.send_email_via_api(
        message=f'Welcome {user.get_full_name()}!',
        email=user.email,
        subject='Welcome to PX360'
    )
    
    if log and log.status == 'sent':
        print("Email sent successfully via API")
    else:
        print("Failed to send email")
```

### Example 2: Send SMS Verification Code

```python
def send_verification_code(phone, code):
    from apps.notifications.services import NotificationService
    
    log = NotificationService.send_sms_via_api(
        message=f'Your verification code is: {code}',
        phone=phone
    )
    
    return log.status == 'sent'
```

### Example 3: Send Complaint Notification via API

```python
def notify_complaint_department(complaint):
    from apps.notifications.services import NotificationService
    
    department = complaint.department
    log = NotificationService.send_email_via_api(
        message=f'New complaint received from {complaint.patient.get_full_name()}',
        email=department.contact_email,
        subject=f'New Complaint: {complaint.ticket_number}',
        related_object=complaint,
        metadata={
            'complaint_id': str(complaint.id),
            'ticket_number': complaint.ticket_number
        }
    )
    
    return log
```

### Example 4: Send Survey Invitation via API

```python
def send_survey_invitation_via_api(survey_instance):
    from apps.notifications.services import NotificationService
    
    patient = survey_instance.patient
    survey_url = survey_instance.get_survey_url()
    
    if survey_instance.delivery_channel == 'email':
        log = NotificationService.send_email_via_api(
            message=f'Please complete your survey: {survey_url}',
            email=patient.email,
            subject='Experience Survey',
            related_object=survey_instance
        )
    elif survey_instance.delivery_channel == 'sms':
        log = NotificationService.send_sms_via_api(
            message=f'Complete your survey: {survey_url}',
            phone=patient.phone,
            related_object=survey_instance
        )
    
    return log
```

## Troubleshooting

### API Disabled

**Problem**: Emails/SMS not being sent via API

**Solution**: Check that `EMAIL_API_ENABLED` or `SMS_API_ENABLED` is set to `true` in `.env`

```bash
# Check environment variable
echo $EMAIL_API_ENABLED

# Enable if disabled
EMAIL_API_ENABLED=true
```

### Authentication Failures

**Problem**: API returning 401 or 403 errors

**Solution**: Verify API key and auth method

```bash
# Check auth method
EMAIL_API_AUTH_METHOD=bearer  # or api_key

# Verify API key is correct
EMAIL_API_KEY=your-actual-api-key
```

### Connection Timeout

**Problem**: Requests timing out

**Solution**: Increase timeout value

```bash
EMAIL_API_TIMEOUT=30  # Increase from 10 to 30 seconds
```

### Frequent Failures

**Problem**: API calls failing consistently

**Solution**: Check logs for specific error messages

```bash
# View logs
tail -f logs/px360.log | grep "API"

# Check NotificationLog in Django admin
# Navigate to /admin/notifications/notificationlog/
```

### No API Calls Being Made

**Problem**: Methods returning `None`

**Solution**: Verify configuration is loaded

```python
from django.conf import settings

# Check configuration
print(settings.EXTERNAL_NOTIFICATION_API)
# Should show 'email' and 'sms' keys with configuration
```

## Migration Guide

### Migrating from Django SMTP to API

**Before** (using Django SMTP):
```python
NotificationService.send_email(
    email='user@example.com',
    subject='Welcome',
    message='Hello!'
)
```

**After** (using external API):
```python
NotificationService.send_email_via_api(
    message='Hello!',
    email='user@example.com',
    subject='Welcome'
)
```

Note: The parameter order is slightly different - `message` comes before `email` for API methods.

### Gradual Migration Strategy

You can use both methods in parallel:

```python
def send_email(email, subject, message):
    from apps.notifications.services import NotificationService
    from django.conf import settings
    
    # Use API if enabled
    if settings.EXTERNAL_NOTIFICATION_API['email']['enabled']:
        return NotificationService.send_email_via_api(
            message=message,
            email=email,
            subject=subject
        )
    # Fall back to Django SMTP
    else:
        return NotificationService.send_email(
            email=email,
            subject=subject,
            message=message
        )
```

## Security Considerations

1. **API Keys**: Never commit `.env` file to version control
2. **HTTPS**: Always use HTTPS URLs for API endpoints
3. **Authentication**: Use Bearer tokens or API keys, never basic auth
4. **Logging**: API keys are not logged in full (metadata stores auth_method, not key)
5. **Timeouts**: Set reasonable timeouts to prevent hanging requests

## Performance Impact

- **Network Latency**: Each API call adds network round-trip time
- **Retries**: Failed requests are retried with exponential backoff
- **Logging**: Minimal overhead from logging to database
- **Async Consideration**: For high-volume scenarios, consider using Celery tasks

## Future Enhancements

Potential improvements for the API notification service:

1. **Async Support**: Integrate with Celery for background processing
2. **Webhooks**: Support for delivery status callbacks
3. **Templates**: Built-in email/SMS template support
4. **Rate Limiting**: Implement API rate limiting
5. **Bulk Operations**: Support for batch email/SMS sending
6. **Provider SDKs**: Integrate provider-specific SDKs (SendGrid, Twilio, etc.)

## Support

For issues or questions:

1. Check logs: `logs/px360.log` and `logs/integrations.log`
2. Verify `.env` configuration
3. Check `NotificationLog` entries in Django admin
4. Test API endpoint independently (curl, Postman, etc.)

## Related Documentation

- [Notification Service](../apps/notifications/README.md)
- [Environment Configuration](../config/settings/DJANGO_ENVIRON_CONFIG.md)
- [Logging Configuration](../config/settings/base.py#logging-configuration)
