# Error Handling

## Overview

The framework provides structured error handling patterns that ensure consistent error management across all application layers. Error handling is designed to be explicit, type-safe, and informative.

## Error Types

### Validation Errors

Validation errors occur when input data doesn't meet business rules or format requirements:

```go
type ValidationError struct {
    Field   string
    Message string
    Value   interface{}
}
```

**Common scenarios:**
- Required fields missing
- Invalid data formats
- Business rule violations
- Field length constraints

### Database Errors

Database operations can fail for various reasons:

```go
type DatabaseError struct {
    Operation string
    Table     string
    Err       error
}
```

**Common scenarios:**
- Connection failures
- Constraint violations
- Record not found
- Transaction rollbacks

### Business Logic Errors

Custom errors for specific business scenarios:

```go
type BusinessError struct {
    Code    string
    Message string
    Context map[string]interface{}
}
```

**Examples:**
- Insufficient permissions
- Resource conflicts
- State transition violations
- Quota exceeded

## Error Handling Layers

### Service Layer

Services return structured errors that can be handled appropriately by calling layers:

**Fetchers**
- Return `nil, false, nil` for not found (not an error)
- Return `nil, false, error` for actual errors
- Distinguish between "not found" and "error occurred"

**Handlers**
- Wrap database errors with operation context
- Return specific errors for constraint violations
- Handle transaction rollbacks gracefully

**Validators**
- Return detailed validation errors with field information
- Support multiple validation errors
- Provide actionable error messages

### Maestro Layer

Maestros orchestrate error handling across multiple services:

```go
func (this *CreateGardenMae) Execute(ctx context.Context, input *CreateGardenMaeInput) (*CreateGardenMaeOutput, error) {
    // Step 1: Validation
    if err := this.GardenValidator.ValidateCreateForm(input.Form); err != nil {
        return nil, fmt.Errorf("validation failed: %w", err)
    }

    // Step 2: Business logic
    if err := this.checkBusinessRules(ctx, input.Form); err != nil {
        return nil, fmt.Errorf("business rule violation: %w", err)
    }

    // Step 3: Persistence
    garden, err := this.createGarden(ctx, input.Form)
    if err != nil {
        return nil, fmt.Errorf("failed to create garden: %w", err)
    }

    return &CreateGardenMaeOutput{Garden: garden}, nil
}
```

### Controller Layer

Controllers translate errors into appropriate HTTP responses:

**Error Response Mapping:**
- **400 Bad Request** - Validation errors
- **404 Not Found** - Resource not found
- **409 Conflict** - Business rule violations
- **500 Internal Server Error** - System errors

## Error Response Patterns

### Structured Error Responses

Controllers return consistent error response formats:

```go
func (gc *GardenController) handleError(c *gin.Context, err error) {
    var validationErr *ValidationError
    var businessErr *BusinessError
    var dbErr *DatabaseError

    switch {
    case errors.As(err, &validationErr):
        c.JSON(400, gin.H{
            "error": "Validation failed",
            "field": validationErr.Field,
            "message": validationErr.Message,
            "code": "VALIDATION_ERROR",
        })

    case errors.As(err, &businessErr):
        c.JSON(409, gin.H{
            "error": businessErr.Message,
            "code": businessErr.Code,
            "context": businessErr.Context,
        })

    case errors.As(err, &dbErr):
        // Log detailed error, return generic message
        log.Printf("Database error: %v", dbErr)
        c.JSON(500, gin.H{
            "error": "Internal server error",
            "code": "INTERNAL_ERROR",
        })

    default:
        // Unknown error - log and return generic response
        log.Printf("Unknown error: %v", err)
        c.JSON(500, gin.H{
            "error": "Internal server error",
            "code": "UNKNOWN_ERROR",
        })
    }
}
```

### API Error Format

Standard API error response structure:

```json
{
  "error": "Human readable error message",
  "code": "MACHINE_READABLE_CODE",
  "field": "field_name",
  "details": {
    "additional": "context information"
  }
}
```

### HTML Error Handling

For web pages, errors are displayed in user-friendly formats:

```go
func (gc *GardenController) Create(c *gin.Context) {
    form := &forms.CreateGardenForm{}
    if err := c.ShouldBind(form); err != nil {
        c.HTML(400, "gardens/new.html", gin.H{
            "error": "Please check your input and try again",
            "form": form,
        })
        return
    }

    output, err := gc.CreateGardenMae.Execute(c.Request.Context(), &maes.CreateGardenMaeInput{
        Form: form,
    })

    if err != nil {
        c.HTML(400, "gardens/new.html", gin.H{
            "error": err.Error(),
            "form": form,
        })
        return
    }

    c.Redirect(302, "/gardens/" + output.Garden.Id)
}
```

## Error Context and Wrapping

### Error Wrapping

Use error wrapping to maintain error chains while adding context:

```go
// In services
func (h *GardenHandler) Create(ctx context.Context, garden *mdl.Garden, mod *handler.HandlerMod) (*mdl.Garden, error) {
    createdGarden, err := h.baseHandler.Create(ctx, garden, mod)
    if err != nil {
        return nil, fmt.Errorf("failed to create garden '%s': %w", garden.Name, err)
    }
    return createdGarden, nil
}

// In Maestros
func (m *CreateGardenMae) Execute(ctx context.Context, input *CreateGardenMaeInput) (*CreateGardenMaeOutput, error) {
    garden, err := m.GardenHandler.Create(ctx, gardenEntity, nil)
    if err != nil {
        return nil, fmt.Errorf("garden creation failed for user %s: %w", input.UserId, err)
    }
    // ...
}
```

### Context Information

Include relevant context in errors:

```go
type ContextualError struct {
    Operation string
    EntityId  string
    UserId    string
    Timestamp time.Time
    Err       error
}

func (e *ContextualError) Error() string {
    return fmt.Sprintf("operation '%s' failed for entity %s (user: %s) at %v: %v",
        e.Operation, e.EntityId, e.UserId, e.Timestamp, e.Err)
}
```

## Logging and Monitoring

### Error Logging

Log errors with appropriate detail levels:

```go
func (gc *GardenController) handleError(c *gin.Context, err error) {
    requestID := c.GetString("request_id")
    userID := c.GetString("user_id")

    // Log with context
    log.WithFields(log.Fields{
        "request_id": requestID,
        "user_id": userID,
        "path": c.Request.URL.Path,
        "error": err.Error(),
    }).Error("Request failed")

    // Return appropriate response...
}
```

### Error Monitoring

Track error patterns for system health:

```go
type ErrorTracker struct {
    errorCounts map[string]int
    mutex       sync.RWMutex
}

func (et *ErrorTracker) RecordError(errorType string) {
    et.mutex.Lock()
    defer et.mutex.Unlock()
    et.errorCounts[errorType]++
}

func (et *ErrorTracker) GetErrorStats() map[string]int {
    et.mutex.RLock()
    defer et.mutex.RUnlock()

    stats := make(map[string]int)
    for k, v := range et.errorCounts {
        stats[k] = v
    }
    return stats
}
```

## Recovery and Resilience

### Panic Recovery

Handle panics gracefully in middleware:

```go
func RecoveryMiddleware() gin.HandlerFunc {
    return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
        if err, ok := recovered.(string); ok {
            log.Printf("Panic recovered: %s", err)
        }

        c.JSON(500, gin.H{
            "error": "Internal server error",
            "code": "PANIC_RECOVERED",
        })
    })
}
```

### Transaction Rollback

Ensure database consistency during errors:

```go
func (m *ComplexGardenMae) Execute(ctx context.Context, input *ComplexGardenMaeInput) (*ComplexGardenMaeOutput, error) {
    tx, err := m.SqlDb.Begin(ctx)
    if err != nil {
        return nil, fmt.Errorf("failed to begin transaction: %w", err)
    }
    defer tx.Rollback() // Rollback if not committed

    handlerMod := &handler.HandlerMod{Tx: tx}

    // Perform operations...
    garden, err := m.GardenHandler.Create(ctx, gardenEntity, handlerMod)
    if err != nil {
        return nil, fmt.Errorf("failed to create garden: %w", err)
    }

    // Commit transaction
    if err := tx.Commit(); err != nil {
        return nil, fmt.Errorf("failed to commit transaction: %w", err)
    }

    return &ComplexGardenMaeOutput{Garden: garden}, nil
}
```

## Testing Error Scenarios

### Unit Testing Errors

Test error conditions explicitly:

```go
func TestCreateGardenMae_ValidationError(t *testing.T) {
    mae := setupCreateGardenMae()

    input := &maes.CreateGardenMaeInput{
        Form: &forms.CreateGardenForm{
            Name: "", // Invalid - empty name
        },
    }

    output, err := mae.Execute(context.Background(), input)

    assert.Error(t, err)
    assert.Nil(t, output)

    var validationErr *ValidationError
    assert.True(t, errors.As(err, &validationErr))
    assert.Equal(t, "name", validationErr.Field)
}
```

### Integration Testing

Test complete error flows:

```go
func TestGardenController_CreateValidationError(t *testing.T) {
    container := setupTestContainer()
    router := setupTestRouter(container)

    w := httptest.NewRecorder()
    req := httptest.NewRequest("POST", "/gardens", strings.NewReader(`{"name": ""}`))
    req.Header.Set("Content-Type", "application/json")

    router.ServeHTTP(w, req)

    assert.Equal(t, 400, w.Code)

    var response map[string]interface{}
    json.Unmarshal(w.Body.Bytes(), &response)
    assert.Equal(t, "VALIDATION_ERROR", response["code"])
}
```

## Best Practices

### Error Design Principles

1. **Be Explicit** - Don't hide errors or fail silently
2. **Be Specific** - Provide actionable error information
3. **Be Consistent** - Use standard error types and responses
4. **Be Secure** - Don't expose sensitive information in errors
5. **Be Helpful** - Include context and suggestions when possible

### Common Patterns

- **Fail Fast** - Validate early and return errors immediately
- **Error Boundaries** - Handle errors at appropriate layers
- **Graceful Degradation** - Provide fallbacks when possible
- **User-Friendly Messages** - Translate technical errors for end users
- **Comprehensive Logging** - Log enough detail for debugging

Error handling is fundamental to building reliable applications. The framework's structured approach ensures consistent, maintainable error management across all application layers.