package handler import ( "errors" "strconv" "accounting-app/pkg/api" "accounting-app/internal/service" "github.com/gin-gonic/gin" ) // RepaymentHandler handles HTTP requests for repayment plan operations type RepaymentHandler struct { repaymentService *service.RepaymentService } // NewRepaymentHandler creates a new RepaymentHandler instance func NewRepaymentHandler(repaymentService *service.RepaymentService) *RepaymentHandler { return &RepaymentHandler{ repaymentService: repaymentService, } } // CreateRepaymentPlan handles POST /api/v1/repayment-plans // Creates a new repayment plan for a bill func (h *RepaymentHandler) CreateRepaymentPlan(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } var input service.CreateRepaymentPlanInput if err := c.ShouldBindJSON(&input); err != nil { api.ValidationError(c, "Invalid request body: "+err.Error()) return } plan, err := h.repaymentService.CreateRepaymentPlan(userId.(uint), input) if err != nil { if errors.Is(err, service.ErrInvalidInstallmentCount) { api.BadRequest(c, "Installment count must be at least 2") return } if errors.Is(err, service.ErrBillAlreadyPaid) { api.BadRequest(c, "Bill is already paid") return } if errors.Is(err, service.ErrPlanAlreadyExists) { api.Conflict(c, "Repayment plan already exists for this bill") return } api.InternalError(c, "Failed to create repayment plan: "+err.Error()) return } api.Created(c, plan) } // GetRepaymentPlan handles GET /api/v1/repayment-plans/:id // Returns a repayment plan by ID func (h *RepaymentHandler) GetRepaymentPlan(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid plan ID") return } plan, err := h.repaymentService.GetRepaymentPlan(userId.(uint), uint(id)) if err != nil { if errors.Is(err, service.ErrRepaymentPlanNotFound) { api.NotFound(c, "Repayment plan not found") return } api.InternalError(c, "Failed to get repayment plan: "+err.Error()) return } api.Success(c, plan) } // GetActivePlans handles GET /api/v1/repayment-plans // Returns all active repayment plans func (h *RepaymentHandler) GetActivePlans(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } plans, err := h.repaymentService.GetActivePlans(userId.(uint)) if err != nil { api.InternalError(c, "Failed to get active plans: "+err.Error()) return } api.Success(c, plans) } // CancelRepaymentPlan handles DELETE /api/v1/repayment-plans/:id // Cancels a repayment plan func (h *RepaymentHandler) CancelRepaymentPlan(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid plan ID") return } if err := h.repaymentService.CancelRepaymentPlan(userId.(uint), uint(id)); err != nil { if errors.Is(err, service.ErrRepaymentPlanNotFound) { api.NotFound(c, "Repayment plan not found") return } api.InternalError(c, "Failed to cancel repayment plan: "+err.Error()) return } api.NoContent(c) } // GetDebtSummary handles GET /api/v1/debt-summary // Returns a comprehensive debt summary func (h *RepaymentHandler) GetDebtSummary(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } summary, err := h.repaymentService.GetDebtSummary(userId.(uint)) if err != nil { api.InternalError(c, "Failed to get debt summary: "+err.Error()) return } api.Success(c, summary) } // GetUnreadReminders handles GET /api/v1/payment-reminders // Returns all unread payment reminders func (h *RepaymentHandler) GetUnreadReminders(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } reminders, err := h.repaymentService.GetUnreadReminders(userId.(uint)) if err != nil { api.InternalError(c, "Failed to get reminders: "+err.Error()) return } api.Success(c, reminders) } // MarkReminderAsRead handles PUT /api/v1/payment-reminders/:id/read // Marks a reminder as read func (h *RepaymentHandler) MarkReminderAsRead(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { api.BadRequest(c, "Invalid reminder ID") return } if err := h.repaymentService.MarkReminderAsRead(uint(id)); err != nil { api.InternalError(c, "Failed to mark reminder as read: "+err.Error()) return } api.NoContent(c) } // RegisterRoutes registers repayment routes to the given router group func (h *RepaymentHandler) RegisterRoutes(rg *gin.RouterGroup) { // Repayment plan routes plans := rg.Group("/repayment-plans") { plans.POST("", h.CreateRepaymentPlan) plans.GET("", h.GetActivePlans) plans.GET("/:id", h.GetRepaymentPlan) plans.DELETE("/:id", h.CancelRepaymentPlan) } // Debt summary route rg.GET("/debt-summary", h.GetDebtSummary) // Payment reminder routes reminders := rg.Group("/payment-reminders") { reminders.GET("", h.GetUnreadReminders) reminders.PUT("/:id/read", h.MarkReminderAsRead) } }