# Error Handling Guidelines

This document provides guidelines for handling errors consistently across the ThrillWiki backend.

## Exception Hierarchy

ThrillWiki uses a structured exception hierarchy defined in `apps/core/exceptions.py`:

```
ThrillWikiException (base)
├── ValidationException (400)
├── NotFoundError (404)
├── PermissionDeniedError (403)
├── BusinessLogicError (400)
├── ServiceError (500)
├── ExternalServiceError (502)
├── CacheError (500)
│
├── Domain-specific exceptions:
│   ├── ParkError
│   │   ├── ParkNotFoundError
│   │   └── ParkOperationError
│   ├── RideError
│   │   ├── RideNotFoundError
│   │   └── RideOperationError
│   ├── LocationError
│   │   ├── InvalidCoordinatesError
│   │   └── GeolocationError
│   ├── ReviewError
│   │   ├── ReviewModerationError
│   │   └── DuplicateReviewError
│   └── AccountError
│       ├── InsufficientPermissionsError
│       └── EmailError
```

## Using ErrorHandler

The `ErrorHandler` class in `apps/core/utils/error_handling.py` provides standardized error handling.

### Template Views

```python
from apps.core.utils.error_handling import ErrorHandler
from apps.core.exceptions import ServiceError

def my_view(request):
    try:
        result = SomeService.do_operation(...)
    except ServiceError as e:
        ErrorHandler.handle_view_error(
            request,
            e,
            user_message="The operation failed. Please try again.",
            log_message=f"Service operation failed for user {request.user.id}"
        )
        return redirect("some-fallback")
    except ValidationError as e:
        ErrorHandler.handle_view_error(
            request,
            e,
            user_message="Invalid data provided",
            level="warning"
        )
        return redirect("form-view")
```

### API Views

```python
from apps.core.utils.error_handling import ErrorHandler
from apps.core.exceptions import ServiceError
from rest_framework import status

class MyAPIView(APIView):
    def post(self, request):
        try:
            result = SomeService.do_operation(...)
            return ErrorHandler.api_success_response(
                data=result,
                message="Operation completed successfully"
            )
        except ServiceError as e:
            return ErrorHandler.handle_api_error(
                e,
                user_message="Failed to complete operation",
                status_code=status.HTTP_400_BAD_REQUEST
            )
```

## Best Practices

### 1. Always Catch Specific Exceptions

```python
# Good
try:
    park = ParkService.create_park(...)
except ParkOperationError as e:
    # Handle park-specific error
    pass
except ValidationException as e:
    # Handle validation error
    pass

# Bad
try:
    park = ParkService.create_park(...)
except Exception as e:
    # Too broad - loses error context
    pass
```

### 2. Log with Appropriate Context

```python
# Good
logger.error(
    f"Park creation failed for user {user.id}: {error}",
    exc_info=True,
    extra={"user_id": user.id, "park_name": name}
)

# Bad
logger.error(f"Error: {error}")
```

### 3. Provide Clear User Messages

```python
# Good - User-friendly and actionable
ErrorHandler.handle_view_error(
    request,
    error,
    user_message="Unable to save your changes. Please check your input and try again."
)

# Bad - Technical details exposed to user
ErrorHandler.handle_view_error(
    request,
    error,
    user_message=f"IntegrityError: UNIQUE constraint failed: parks_park.slug"
)
```

### 4. Use Appropriate HTTP Status Codes

| Error Type | Status Code | When to Use |
|------------|-------------|-------------|
| ValidationException | 400 | Invalid user input |
| NotFoundError | 404 | Resource doesn't exist |
| PermissionDeniedError | 403 | User lacks permission |
| BusinessLogicError | 400 | Business rule violation |
| ServiceError | 500 | Internal service failure |
| ExternalServiceError | 502 | Third-party service failure |

### 5. Never Use Bare `except:` Clauses

```python
# Never do this
try:
    something()
except:
    pass

# Always specify exception type
try:
    something()
except SpecificException:
    handle_error()
```

## Error Response Format

### API Error Response

```json
{
    "error": "User-friendly error message",
    "detail": "Technical error details",
    "error_code": "SPECIFIC_ERROR_CODE",
    "details": {
        "field": "Additional context"
    }
}
```

### API Success Response

```json
{
    "status": "success",
    "message": "Operation completed successfully",
    "data": {
        // Response data
    }
}
```

## Creating Custom Exceptions

When creating domain-specific exceptions:

```python
from apps.core.exceptions import BusinessLogicError

class MyDomainError(BusinessLogicError):
    """Raised when my domain operation fails."""

    default_message = "My domain operation failed"
    error_code = "MY_DOMAIN_ERROR"
    status_code = 400

    def __init__(self, context_value: str = None, **kwargs):
        if context_value:
            kwargs["details"] = {"context": context_value}
            kwargs["message"] = f"Operation failed for: {context_value}"
        super().__init__(**kwargs)
```
