package handler import ( "strconv" "accounting-app/pkg/api" "accounting-app/internal/service" "github.com/gin-gonic/gin" ) // PiggyBankHandler handles HTTP requests for piggy bank operations type PiggyBankHandler struct { service *service.PiggyBankService } // NewPiggyBankHandler creates a new PiggyBankHandler instance func NewPiggyBankHandler(service *service.PiggyBankService) *PiggyBankHandler { return &PiggyBankHandler{ service: service, } } // RegisterRoutes registers piggy bank-related routes func (h *PiggyBankHandler) RegisterRoutes(rg *gin.RouterGroup) { piggyBanks := rg.Group("/piggy-banks") { piggyBanks.POST("", h.CreatePiggyBank) piggyBanks.GET("", h.GetAllPiggyBanks) piggyBanks.GET("/progress", h.GetAllPiggyBankProgress) piggyBanks.GET("/:id", h.GetPiggyBank) piggyBanks.PUT("/:id", h.UpdatePiggyBank) piggyBanks.DELETE("/:id", h.DeletePiggyBank) piggyBanks.POST("/:id/deposit", h.Deposit) piggyBanks.POST("/:id/withdraw", h.Withdraw) piggyBanks.GET("/:id/progress", h.GetPiggyBankProgress) } } // CreatePiggyBank handles POST /api/v1/piggy-banks func (h *PiggyBankHandler) CreatePiggyBank(c *gin.Context) { var input service.PiggyBankInput if err := c.ShouldBindJSON(&input); err != nil { api.ValidationError(c, err.Error()) return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } input.UserID = userID.(uint) piggyBank, err := h.service.CreatePiggyBank(input) if err != nil { switch err { case service.ErrInvalidTargetAmount: api.BadRequest(c, err.Error()) case service.ErrInvalidPiggyBankType: api.BadRequest(c, err.Error()) case service.ErrInvalidAutoRule: api.BadRequest(c, err.Error()) case service.ErrAccountNotFound: api.BadRequest(c, err.Error()) default: api.InternalError(c, "Failed to create piggy bank") } return } api.Created(c, piggyBank) } // GetPiggyBank handles GET /api/v1/piggy-banks/:id func (h *PiggyBankHandler) GetPiggyBank(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid piggy bank ID") return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } piggyBank, err := h.service.GetPiggyBank(userID.(uint), uint(id)) if err != nil { if err == service.ErrPiggyBankNotFound { api.NotFound(c, "Piggy bank not found") return } api.InternalError(c, "Failed to get piggy bank") return } api.Success(c, piggyBank) } // GetAllPiggyBanks handles GET /api/v1/piggy-banks func (h *PiggyBankHandler) GetAllPiggyBanks(c *gin.Context) { // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } piggyBanks, err := h.service.GetAllPiggyBanks(userID.(uint)) if err != nil { api.InternalError(c, "Failed to get piggy banks") return } api.Success(c, piggyBanks) } // UpdatePiggyBank handles PUT /api/v1/piggy-banks/:id func (h *PiggyBankHandler) UpdatePiggyBank(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid piggy bank ID") return } var input service.PiggyBankInput if err := c.ShouldBindJSON(&input); err != nil { api.ValidationError(c, err.Error()) return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } input.UserID = userID.(uint) piggyBank, err := h.service.UpdatePiggyBank(userID.(uint), uint(id), input) if err != nil { switch err { case service.ErrPiggyBankNotFound: api.NotFound(c, "Piggy bank not found") case service.ErrInvalidTargetAmount: api.BadRequest(c, err.Error()) case service.ErrInvalidPiggyBankType: api.BadRequest(c, err.Error()) case service.ErrInvalidAutoRule: api.BadRequest(c, err.Error()) case service.ErrAccountNotFound: api.BadRequest(c, err.Error()) default: api.InternalError(c, "Failed to update piggy bank") } return } api.Success(c, piggyBank) } // DeletePiggyBank handles DELETE /api/v1/piggy-banks/:id func (h *PiggyBankHandler) DeletePiggyBank(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid piggy bank ID") return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } err = h.service.DeletePiggyBank(userID.(uint), uint(id)) if err != nil { switch err { case service.ErrPiggyBankNotFound: api.NotFound(c, "Piggy bank not found") case service.ErrPiggyBankInUse: api.Conflict(c, err.Error()) default: api.InternalError(c, "Failed to delete piggy bank") } return } api.NoContent(c) } // Deposit handles POST /api/v1/piggy-banks/:id/deposit func (h *PiggyBankHandler) Deposit(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid piggy bank ID") return } var input service.DepositInput if err := c.ShouldBindJSON(&input); err != nil { api.ValidationError(c, err.Error()) return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } piggyBank, err := h.service.Deposit(userID.(uint), uint(id), input) if err != nil { switch err { case service.ErrPiggyBankNotFound: api.NotFound(c, "Piggy bank not found") case service.ErrInvalidDepositAmount: api.BadRequest(c, err.Error()) case service.ErrAccountNotFound: api.BadRequest(c, err.Error()) case service.ErrInsufficientAccountFunds: api.BadRequest(c, err.Error()) default: api.InternalError(c, "Failed to deposit to piggy bank") } return } api.Success(c, piggyBank) } // Withdraw handles POST /api/v1/piggy-banks/:id/withdraw func (h *PiggyBankHandler) Withdraw(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid piggy bank ID") return } var input service.WithdrawInput if err := c.ShouldBindJSON(&input); err != nil { api.ValidationError(c, err.Error()) return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } piggyBank, err := h.service.Withdraw(userID.(uint), uint(id), input) if err != nil { switch err { case service.ErrPiggyBankNotFound: api.NotFound(c, "Piggy bank not found") case service.ErrInvalidWithdrawAmount: api.BadRequest(c, err.Error()) case service.ErrAccountNotFound: api.BadRequest(c, err.Error()) case service.ErrInsufficientBalance: api.BadRequest(c, err.Error()) default: api.InternalError(c, "Failed to withdraw from piggy bank") } return } api.Success(c, piggyBank) } // GetPiggyBankProgress handles GET /api/v1/piggy-banks/:id/progress func (h *PiggyBankHandler) GetPiggyBankProgress(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid piggy bank ID") return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } progress, err := h.service.GetPiggyBankProgress(userID.(uint), uint(id)) if err != nil { if err == service.ErrPiggyBankNotFound { api.NotFound(c, "Piggy bank not found") return } api.InternalError(c, "Failed to get piggy bank progress") return } api.Success(c, progress) } // GetAllPiggyBankProgress handles GET /api/v1/piggy-banks/progress func (h *PiggyBankHandler) GetAllPiggyBankProgress(c *gin.Context) { // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } progressList, err := h.service.GetAllPiggyBankProgress(userID.(uint)) if err != nil { api.InternalError(c, "Failed to get piggy bank progress") return } api.Success(c, progressList) }