# Error Handling Guidelines for Credora Development

## Introduction
This document provides guidelines for implementing consistent error handling across the Credora application. Following these guidelines ensures a uniform user experience and maintainable codebase.

## General Principles

### 1. Fail Fast
- Validate input data as early as possible
- Throw exceptions immediately when invalid data is detected
- Don't continue processing with invalid data

### 2. User-Friendly Messages
- Error messages should be clear and actionable
- Avoid technical jargon in user-facing messages
- Provide specific guidance on how to fix errors

### 3. Consistent Structure
- Use the established exception hierarchy
- Follow the standard error response format
- Maintain consistent logging patterns

## Exception Usage Guidelines

### When to Use Each Exception Type

#### BusinessLogicException
Use for business rule violations:
```php
// Examples
- Cannot delete client with active loans
- Loan amount exceeds client's credit limit
- Insufficient account balance for withdrawal
- Invalid state transitions (e.g., approving already approved loan)
```

#### ValidationException
Use for form validation failures:
```php
// Examples
- Required fields missing
- Invalid data formats
- Constraint violations (min/max values)
- Uniqueness violations
```

#### AuthorizationException
Use for access control violations:
```php
// Examples
- User lacks required permissions
- Role-based access restrictions
- Attempting operations outside user scope
```

#### NotFoundException
Use when resources don't exist:
```php
// Examples
- Client ID not found
- Loan not found
- Invalid account number
```

### Exception Throwing Best Practices

#### 1. Provide Context
```php
// Good
throw new BusinessLogicException(
    'Cannot disburse loan without approval',
    'LOAN_NOT_APPROVED_FOR_DISBURSEMENT',
    ['loan_id' => $loan->id, 'current_status' => $loan->status]
);

// Bad - Missing context
throw new BusinessLogicException('Loan cannot be disbursed');
```

#### 2. Use Appropriate Error Codes
```php
// Good - Specific error codes
'CLIENT_HAS_ACTIVE_LOANS'
'LOAN_INSUFFICIENT_COLLATERAL'
'ACCOUNT_INSUFFICIENT_FUNDS'

// Bad - Generic codes
'BUSINESS_ERROR'
'VALIDATION_ERROR'
```

#### 3. Include Relevant Data
```php
// For validation errors, include field-specific messages
throw new ValidationException(
    'Client registration validation failed',
    'CLIENT_VALIDATION_FAILED',
    $validator->errors()->toArray()
);
```

## Form Request Classes

### When to Create Form Requests
- Complex validation logic (>3 validation rules)
- Custom validation messages needed
- Business rules that span multiple fields
- File upload validation

### Form Request Structure
```php
class StoreLoanRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true; // Or implement authorization logic
    }

    public function rules(): array
    {
        return [
            // Validation rules
        ];
    }

    public function messages(): array
    {
        return [
            // Custom messages
        ];
    }

    protected function failedValidation(Validator $validator)
    {
        throw new ValidationException(
            'Validation failed',
            'VALIDATION_ERROR_CODE',
            $validator->errors()->toArray()
        );
    }
}
```

## Controller Error Handling

### Controller Base Class
All controllers should extend the base `Controller` class which provides:
- Consistent logging methods
- Error response helpers
- Flash message utilities

### Error Handling Patterns

#### 1. Try-Catch Blocks
```php
public function store(StoreLoanRequest $request)
{
    try {
        DB::beginTransaction();

        // Business logic here

        DB::commit();

        return redirect()->route('loans.index')
            ->with('success', 'Loan created successfully');

    } catch (BusinessLogicException $e) {
        DB::rollBack();

        $this->logBusiness('warning', 'Loan creation failed', [
            'error_code' => $e->getErrorCode(),
            'client_id' => $request->client_id
        ]);

        return $this->backWithError($e->getMessage());

    } catch (Exception $e) {
        DB::rollBack();

        $this->logBusiness('error', 'Unexpected error during loan creation', [
            'error' => $e->getMessage(),
            'client_id' => $request->client_id
        ]);

        return $this->backWithError('An unexpected error occurred. Please try again.');
    }
}
```

#### 2. Validation in Controllers
```php
public function update(Request $request, Loan $loan)
{
    // Manual validation when form request not suitable
    $validator = Validator::make($request->all(), [
        'amount' => 'required|numeric|min:1000',
        'status' => 'required|in:pending,approved,rejected'
    ]);

    if ($validator->fails()) {
        throw new ValidationException(
            'Loan update validation failed',
            'LOAN_UPDATE_VALIDATION_FAILED',
            $validator->errors()->toArray()
        );
    }

    // Proceed with update
}
```

## Logging Guidelines

### Log Levels
- **ERROR**: System failures, unexpected exceptions
- **WARNING**: Business rule violations, security issues
- **INFO**: Normal operations, validation failures
- **DEBUG**: Detailed debugging information

### Logging Context
Always include relevant context:
```php
$this->logAudit('loan_approved', [
    'loan_id' => $loan->id,
    'client_id' => $loan->client_id,
    'approved_by' => auth()->id(),
    'amount' => $loan->amount
]);
```

### Sensitive Data
- Never log passwords, PINs, or sensitive financial data
- Mask sensitive information in logs
- Use user IDs instead of personal information

## Testing Error Scenarios

### Unit Tests
```php
/** @test */
public function it_throws_exception_for_invalid_loan_amount()
{
    $this->expectException(BusinessLogicException::class);
    $this->expectExceptionCode('LOAN_AMOUNT_TOO_HIGH');

    // Test code that should throw exception
}
```

### Feature Tests
```php
/** @test */
public function it_returns_validation_errors_for_invalid_data()
{
    $response = $this->postJson('/api/loans', [
        'amount' => 'invalid_amount'
    ]);

    $response->assertStatus(422)
        ->assertJsonStructure([
            'success',
            'message',
            'error_code',
            'errors'
        ]);
}
```

## API Error Responses

### Consistent Response Format
```json
{
  "success": false,
  "message": "Clear, user-friendly message",
  "error_code": "MACHINE_READABLE_CODE",
  "errors": {
    "field_name": ["Field-specific error messages"]
  },
  "timestamp": "ISO 8601 timestamp"
}
```

### Error Codes Convention
- Use UPPER_SNAKE_CASE
- Prefix with module name (LOAN_, CLIENT_, SAVINGS_)
- Be specific but not verbose

## Frontend Integration

### Handling API Errors
```javascript
// Example error handling in frontend
fetch('/api/loans', {
    method: 'POST',
    body: formData
})
.then(response => response.json())
.then(data => {
    if (!data.success) {
        // Handle error
        showError(data.message);

        // Handle field errors
        if (data.errors) {
            Object.keys(data.errors).forEach(field => {
                showFieldError(field, data.errors[field][0]);
            });
        }
    } else {
        // Success handling
    }
});
```

### User Feedback
- Display error messages prominently
- Highlight fields with validation errors
- Provide clear next steps for users
- Don't expose technical details

## Common Pitfalls to Avoid

### 1. Generic Exception Handling
```php
// Bad
catch (Exception $e) {
    return response()->json(['error' => 'Something went wrong']);
}

// Good
catch (BusinessLogicException $e) {
    return $this->errorResponse($e->getMessage(), $e->getErrorCode());
}
```

### 2. Inconsistent Error Messages
```php
// Bad - Different messages for same error
"Invalid amount" // in one place
"Amount must be valid" // in another

// Good - Consistent messaging
"Loan amount must be between KSh 1,000 and KSh 10,000,000"
```

### 3. Missing Context in Logs
```php
// Bad
Log::error('Loan creation failed');

// Good
Log::error('Loan creation failed', [
    'client_id' => $clientId,
    'amount' => $amount,
    'user_id' => auth()->id()
]);
```

### 4. Exposing Sensitive Information
```php
// Bad
throw new Exception('Invalid password for user: ' . $user->email);

// Good
throw new AuthorizationException('Invalid credentials');
```

## Maintenance

### Regular Reviews
- Review error logs monthly for patterns
- Update error messages based on user feedback
- Ensure error codes remain unique
- Test error scenarios after code changes

### Documentation Updates
- Update this guide when adding new exception types
- Document new error codes
- Include examples for new patterns

### Monitoring
- Set up alerts for error rate spikes
- Monitor for new error types
- Track user-reported issues
- Measure error resolution time
