# Controllers

## Overview

Controllers handle HTTP requests, orchestrate business logic through Mae modules, and format responses. They serve as the bridge between the HTTP layer and business logic.

## Controller Structure

### Basic Controller Pattern
```go
type GardenController struct {
    // Business Logic
    CreateGardenMae *mae.CreateGardenMae
    UpdateGardenMae *mae.UpdateGardenMae
    DeleteGardenMae *mae.DeleteGardenMae
    
    // Data Services
    GardenFetcher   *fetchers.GardenFetcher
    GardenHydrator  *hydrators.GardenHydrator
    GardenSerializer *serializers.GardenSerializer
    
    // Request Handling
    GardenBinder    *binders.GardenBinder
    GardenPresenter *presenters.GardenPresenter
    
    // Utilities
    AppUrler        *urlers.AppUrler
    Logger          *logger.Logger
}
```

## CRUD Operations

### Create Action
```go
func (this *GardenController) Create(c *gin.Context) {
    // 1. Bind form data
    form := &forms.CreateGardenForm{}
    if err := this.GardenBinder.BindCreateForm(c, form); err != nil {
        c.JSON(400, gin.H{"error": "Invalid form data", "details": err.Error()})
        return
    }
    
    // 2. Execute business logic
    input := &maes.CreateGardenMaeInput{
        Form:   form,
        UserId: this.getCurrentUserId(c),
    }
    
    output, err := this.CreateGardenMae.Execute(c.Request.Context(), input)
    if err != nil {
        this.handleError(c, err)
        return
    }
    
    // 3. Format response
    location := this.AppUrler.ShowGarden(output.Garden.Id)
    c.Header("Location", location)
    c.JSON(201, gin.H{
        "garden": output.Garden,
        "success": true,
        "redirect": location,
    })
}
```

### Show Action
```go
func (this *GardenController) Show(c *gin.Context) {
    // 1. Extract parameters
    id := c.Param("id")
    if id == "" {
        c.JSON(400, gin.H{"error": "Garden ID is required"})
        return
    }
    
    // 2. Fetch entity
    garden, exists, err := this.GardenFetcher.FindOneById(c.Request.Context(), id, nil)
    if err != nil {
        c.JSON(500, gin.H{"error": "Failed to fetch garden"})
        return
    }
    
    if !exists {
        c.JSON(404, gin.H{"error": "Garden not found"})
        return
    }
    
    // 3. Hydrate relationships
    err = this.GardenHydrator.OneViaPreset(c.Request.Context(), garden, "show", nil)
    if err != nil {
        this.Logger.Error("Failed to hydrate garden relationships", "error", err)
        // Continue without relationships
    }
    
    // 4. Render response
    c.HTML(200, "gardens/show.html", gin.H{
        "title":  fmt.Sprintf("Garden: %s", garden.Name),
        "garden": garden,
    })
}
```

### List Action
```go
func (this *GardenController) List(c *gin.Context) {
    // 1. Build query parameters
    mod := this.GardenFetcher.Mod()
    
    // Search filter
    if search := c.Query("search"); search != "" {
        mod.ContainsStringValueFilter("name", search)
    }
    
    // Pagination
    if page, err := strconv.Atoi(c.DefaultQuery("page", "1")); err == nil && page > 0 {
        mod.Page = page
    }
    
    // 2. Fetch data
    gardens, pagination, err := this.GardenFetcher.FindPage(c.Request.Context(), mod)
    if err != nil {
        c.JSON(500, gin.H{"error": "Failed to fetch gardens"})
        return
    }
    
    // 3. Hydrate for list view
    err = this.GardenHydrator.ManyViaPreset(c.Request.Context(), gardens, "list", nil)
    if err != nil {
        this.Logger.Error("Failed to hydrate gardens", "error", err)
    }
    
    // 4. Render response
    c.HTML(200, "gardens/list.html", gin.H{
        "title":      "Gardens",
        "gardens":    gardens,
        "pagination": pagination,
        "search":     c.Query("search"),
    })
}
```

### Update Action
```go
func (this *GardenController) Update(c *gin.Context) {
    // 1. Extract ID and bind form
    id := c.Param("id")
    form := &forms.UpdateGardenForm{Id: id}
    
    if err := this.GardenBinder.BindUpdateForm(c, form); err != nil {
        c.JSON(400, gin.H{"error": "Invalid form data", "details": err.Error()})
        return
    }
    
    // 2. Execute update logic
    output, err := this.UpdateGardenMae.Execute(c.Request.Context(), &maes.UpdateGardenMaeInput{
        Form: form,
    })
    if err != nil {
        this.handleError(c, err)
        return
    }
    
    // 3. Handle response format
    if this.acceptsJSON(c) {
        c.JSON(200, gin.H{
            "garden": output.Garden,
            "success": true,
        })
    } else {
        c.Redirect(302, this.AppUrler.ShowGarden(output.Garden.Id))
    }
}
```

### Delete Action
```go
func (this *GardenController) Delete(c *gin.Context) {
    id := c.Param("id")
    
    // Execute delete logic
    err := this.DeleteGardenMae.Execute(c.Request.Context(), &maes.DeleteGardenMaeInput{
        Id: id,
    })
    if err != nil {
        this.handleError(c, err)
        return
    }
    
    if this.acceptsJSON(c) {
        c.JSON(204, nil) // No content
    } else {
        c.Redirect(302, this.AppUrler.ListGardens())
    }
}
```

## Form Handling

### Form Display Actions
```go
func (this *GardenController) ShowNew(c *gin.Context) {
    // Show create form
    form := &forms.CreateGardenForm{}
    
    c.HTML(200, "gardens/new.html", gin.H{
        "title": "New Garden",
        "form":  form,
    })
}

func (this *GardenController) ShowEdit(c *gin.Context) {
    id := c.Param("id")
    
    // Load existing garden
    garden, exists, err := this.GardenFetcher.FindOneById(c.Request.Context(), id, nil)
    if err != nil || !exists {
        c.Redirect(302, this.AppUrler.ListGardens())
        return
    }
    
    // Populate form from garden
    form := &forms.UpdateGardenForm{}
    form = this.GardenMolder.ToForm(form, garden)
    
    c.HTML(200, "gardens/edit.html", gin.H{
        "title":  "Edit Garden",
        "garden": garden,
        "form":   form,
    })
}
```

## API vs Web Responses

### Dual Response Methods
```go
// Web interface (HTML)
func (this *GardenController) List(c *gin.Context) {
    gardens := this.loadGardens(c)
    c.HTML(200, "gardens/list.html", gin.H{"gardens": gardens})
}

// API interface (JSON)
func (this *GardenController) ApiList(c *gin.Context) {
    gardens := this.loadGardens(c)
    
    jsonData, err := this.GardenSerializer.SetToJson(gardens)
    if err != nil {
        c.JSON(500, gin.H{"error": "Serialization failed"})
        return
    }
    
    c.Data(200, "application/json", jsonData)
}
```

### Content Negotiation
```go
func (this *GardenController) Show(c *gin.Context) {
    garden := this.loadGarden(c)
    
    if this.acceptsJSON(c) {
        jsonData, _ := this.GardenSerializer.OneToJson(garden)
        c.Data(200, "application/json", jsonData)
    } else {
        c.HTML(200, "gardens/show.html", gin.H{"garden": garden})
    }
}

func (this *GardenController) acceptsJSON(c *gin.Context) bool {
    return strings.Contains(c.GetHeader("Accept"), "application/json")
}
```

## Error Handling

### Structured Error Handling
```go
func (this *GardenController) handleError(c *gin.Context, err error) {
    switch e := err.(type) {
    case *ValidationError:
        c.JSON(400, gin.H{
            "error": "Validation failed",
            "field": e.Field,
            "message": e.Message,
        })
    case *NotFoundError:
        c.JSON(404, gin.H{
            "error": "Garden not found",
        })
    case *BusinessRuleError:
        c.JSON(422, gin.H{
            "error": e.Message,
            "code":  e.Code,
        })
    default:
        this.Logger.Error("Unexpected error", "error", err)
        c.JSON(500, gin.H{
            "error": "Internal server error",
        })
    }
}
```

## Helper Methods

### Common Patterns
```go
func (this *GardenController) getCurrentUserId(c *gin.Context) string {
    user, exists := c.Get("user")
    if !exists {
        return ""
    }
    return user.(*mdl.User).Id
}

func (this *GardenController) requireAuthentication(c *gin.Context) *mdl.User {
    user, exists := c.Get("user")
    if !exists {
        c.JSON(401, gin.H{"error": "Authentication required"})
        c.Abort()
        return nil
    }
    return user.(*mdl.User)
}

func (this *GardenController) requireOwnership(c *gin.Context, garden *mdl.Garden) bool {
    user := this.requireAuthentication(c)
    if user == nil {
        return false
    }
    
    if garden.UserId != user.Id {
        c.JSON(403, gin.H{"error": "Access denied"})
        c.Abort()
        return false
    }
    
    return true
}
```

## Testing Controllers

### HTTP Testing
```go
func TestGardenController_Create(t *testing.T) {
    // Setup
    controller := setupTestController()
    router := gin.New()
    router.POST("/gardens", controller.Create)
    
    // Create request
    body := `{"name": "Test Garden", "description": "A test"}`
    req, _ := http.NewRequest("POST", "/gardens", strings.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    
    w := httptest.NewRecorder()
    router.ServeHTTP(w, req)
    
    // Assert
    assert.Equal(t, 201, w.Code)
    
    var response map[string]interface{}
    json.Unmarshal(w.Body.Bytes(), &response)
    assert.True(t, response["success"].(bool))
}
```

## LLM Controller Development Notes

- Controllers orchestrate but don't contain business logic
- Use Mae modules for complex business operations
- Handle both HTML and JSON responses appropriately
- Implement proper error handling with structured responses
- Extract common patterns into helper methods
- Test controllers with HTTP requests
- Follow RESTful conventions for action naming