package handler import ( "errors" "strconv" "accounting-app/internal/models" "accounting-app/internal/service" "accounting-app/pkg/api" "github.com/gin-gonic/gin" ) // NotificationHandler handles HTTP requests for notification operations type NotificationHandler struct { notificationService *service.NotificationService } // NewNotificationHandler creates a new NotificationHandler instance func NewNotificationHandler(notificationService *service.NotificationService) *NotificationHandler { return &NotificationHandler{ notificationService: notificationService, } } // GetNotifications handles GET /api/v1/notifications // Returns a list of notifications with pagination and filtering func (h *NotificationHandler) GetNotifications(c *gin.Context) { // Get user ID from context userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } // Parse query parameters input := service.NotificationListInput{} // Parse type filter if typeStr := c.Query("type"); typeStr != "" { notifType := models.NotificationType(typeStr) input.Type = ¬ifType } // Parse is_read filter if isReadStr := c.Query("is_read"); isReadStr != "" { isRead := isReadStr == "true" input.IsRead = &isRead } // Parse pagination if offsetStr := c.Query("offset"); offsetStr != "" { offset, err := strconv.Atoi(offsetStr) if err != nil || offset < 0 { api.BadRequest(c, "Invalid offset") return } input.Offset = offset } if limitStr := c.Query("limit"); limitStr != "" { limit, err := strconv.Atoi(limitStr) if err != nil || limit < 0 { api.BadRequest(c, "Invalid limit") return } input.Limit = limit } result, err := h.notificationService.ListNotifications(userID.(uint), input) if err != nil { api.InternalError(c, "Failed to get notifications: "+err.Error()) return } // Calculate total pages totalPages := 0 if result.Limit > 0 { totalPages = int((result.Total + int64(result.Limit) - 1) / int64(result.Limit)) } // Calculate current page (1-indexed) currentPage := 1 if result.Limit > 0 { currentPage = (result.Offset / result.Limit) + 1 } api.SuccessWithMeta(c, result.Notifications, &api.Meta{ Page: currentPage, PageSize: result.Limit, TotalCount: result.Total, TotalPages: totalPages, }) } // GetUnreadCount handles GET /api/v1/notifications/unread-count // Returns the count of unread notifications func (h *NotificationHandler) GetUnreadCount(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } count, err := h.notificationService.GetUnreadCount(userID.(uint)) if err != nil { api.InternalError(c, "Failed to get unread count: "+err.Error()) return } api.Success(c, gin.H{"count": count}) } // MarkAsRead handles PUT /api/v1/notifications/:id/read // Marks a notification as read func (h *NotificationHandler) MarkAsRead(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 notification ID") return } err = h.notificationService.MarkAsRead(userID.(uint), uint(id)) if err != nil { if errors.Is(err, service.ErrNotificationNotFound) { api.NotFound(c, "Notification not found") return } api.InternalError(c, "Failed to mark notification as read: "+err.Error()) return } api.Success(c, gin.H{"message": "Notification marked as read"}) } // MarkAllAsRead handles POST /api/v1/notifications/read-all // Marks all notifications as read func (h *NotificationHandler) MarkAllAsRead(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } err := h.notificationService.MarkAllAsRead(userID.(uint)) if err != nil { api.InternalError(c, "Failed to mark all notifications as read: "+err.Error()) return } api.Success(c, gin.H{"message": "All notifications marked as read"}) } // DeleteNotification handles DELETE /api/v1/notifications/:id // Deletes a notification func (h *NotificationHandler) DeleteNotification(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 notification ID") return } err = h.notificationService.DeleteNotification(userID.(uint), uint(id)) if err != nil { if errors.Is(err, service.ErrNotificationNotFound) { api.NotFound(c, "Notification not found") return } api.InternalError(c, "Failed to delete notification: "+err.Error()) return } api.NoContent(c) } // RegisterRoutes registers all notification routes to the given router group func (h *NotificationHandler) RegisterRoutes(rg *gin.RouterGroup) { notifications := rg.Group("/notifications") { notifications.GET("", h.GetNotifications) notifications.GET("/unread-count", h.GetUnreadCount) notifications.PUT("/:id/read", h.MarkAsRead) notifications.POST("/read-all", h.MarkAllAsRead) notifications.DELETE("/:id", h.DeleteNotification) } }