# API Error Handling Specification

## Overview
All API endpoints now return consistent error responses, regardless of the error type. This standardization improves client-side error handling and debugging.

## Error Response Format

### Standard Error Response
All errors follow this consistent format:

```json
{
  "error": "ERROR_CODE",
  "message": "Human-readable error description",
  "status": 400
}
```

### Fields
- **error** (string): Machine-readable error code for client handling
- **message** (string): Human-readable error message in English
- **status** (integer): HTTP status code

## HTTP Status Codes

| Code | Error Code | Meaning | Use Case |
|------|-----------|---------|----------|
| 400 | INVALID_INPUT | Bad request / validation error | Missing required fields, invalid data format |
| 401 | UNAUTHORIZED | Authentication required | Missing or invalid token |
| 403 | PERMISSION_DENIED | Access forbidden | User lacks required permissions |
| 404 | NOT_FOUND | Resource not found | Record doesn't exist |
| 409 | CONFLICT | Resource conflict | Duplicate email, conflicting data |
| 500 | INTERNAL_ERROR | Server error | Unexpected backend failure |

## Error Codes Reference

### Validation Errors (400)
- `INVALID_INPUT`: Generic invalid input
- `VALIDATION_ERROR`: Serializer validation failed (detailed messages included)

### Authentication (401)
- `UNAUTHORIZED`: Missing or invalid authentication token

### Authorization (403)
- `PERMISSION_DENIED`: User doesn't have permission for this action

### Not Found (404)
- `NOT_FOUND`: Resource doesn't exist

### Conflicts (409)
- `CONFLICT`: Resource conflict (e.g., duplicate email)

### Server Errors (500)
- `INTERNAL_ERROR`: Unexpected server error

## Examples

### Example 1: Validation Error
**Request:**
```bash
curl -X POST http://api.example.com/api/users/register/ \
  -H "Content-Type: application/json" \
  -d '{"username": "john"}'
```

**Response (400):**
```json
{
  "error": "VALIDATION_ERROR",
  "message": "email: This field is required.; password: This field is required.",
  "status": 400
}
```

### Example 2: Missing Authentication
**Request:**
```bash
curl http://api.example.com/api/users/me/
```

**Response (401):**
```json
{
  "error": "UNAUTHORIZED",
  "message": "Authentication credentials were not provided.",
  "status": 401
}
```

### Example 3: Permission Denied
**Request:**
```bash
curl -X PATCH http://api.example.com/api/bookings/123/contract-pdf/ \
  -H "Authorization: Bearer TOKEN"
```

**Response (403):**
```json
{
  "error": "PERMISSION_DENIED",
  "message": "Permission denied",
  "status": 403
}
```

### Example 4: Resource Not Found
**Request:**
```bash
curl http://api.example.com/api/listings/99999/
```

**Response (404):**
```json
{
  "error": "NOT_FOUND",
  "message": "Not found.",
  "status": 404
}
```

### Example 5: Duplicate Email
**Request:**
```bash
curl -X POST http://api.example.com/api/users/register/ \
  -H "Content-Type: application/json" \
  -d '{"username": "john2", "email": "existing@test.com", "password": "SecurePass123!"}'
```

**Response (400):**
```json
{
  "error": "VALIDATION_ERROR",
  "message": "email: A user with this email already exists.",
  "status": 400
}
```

## Implementation Details

### Global Exception Handler
The error handler is configured in `config/error_handlers.py` and registered in Django settings:

```python
REST_FRAMEWORK = {
    ...
    "EXCEPTION_HANDLER": "config.error_handlers.exception_handler",
    ...
}
```

### Custom Exception Classes
Use these custom exceptions in views for consistent error responses:

```python
from config.error_handlers import InvalidInput, PermissionDenied, ResourceNotFound

# Example: Invalid input
if not listing.instant_book:
    raise InvalidInput("Listing does not support instant booking")

# Example: Permission denied
if booking.traveler != request.user:
    raise PermissionDenied("You can only access your own bookings")

# Example: Not found
if not Listing.objects.filter(pk=listing_id).exists():
    raise ResourceNotFound("Listing not found")
```

### Error Response Utility
Use `error_response()` for manual error responses:

```python
from config.error_handlers import error_response

def custom_endpoint(request):
    if not valid_request(request):
        return error_response(
            message="Invalid request parameters",
            code="INVALID_REQUEST",
            status_code=400
        )
```

## Migration Guide

### Before (Inconsistent)
```python
return Response({"detail": "Listing not found"}, status=404)  # Different format
return Response({"error": "Not found"}, status=404)  # Different format
raise Http404("Listing not found")  # No JSON response
```

### After (Consistent)
```python
from config.error_handlers import ResourceNotFound

raise ResourceNotFound("Listing not found")
# Returns: {"error": "NOT_FOUND", "message": "Listing not found", "status": 404}
```

## Best Practices

1. **Use custom exceptions** instead of manual Response objects
2. **Always include meaningful messages** for debugging
3. **Use correct HTTP status codes** (not all errors are 400)
4. **Log errors for monitoring** (added to error handler)
5. **Test error cases** in your test suite

## Testing Error Responses

```python
def test_duplicate_email_returns_validation_error(self):
    User.objects.create_user(username="john", email="john@test.com", password="pass")
    
    response = self.client.post("/api/users/register/", {
        "username": "jane",
        "email": "john@test.com",
        "password": "SecurePass123!",
    }, format="json")
    
    self.assertEqual(response.status_code, 400)
    self.assertEqual(response.data["error"], "VALIDATION_ERROR")
    self.assertIn("email", response.data["message"])
```
