package repository import ( "context" "time" "gorm.io/gorm" "github.com/gochat/gochat/internal/model" ) // NotificationRepo implements GORM repository for Notification. // Reference: Chatwoot app/models/notification.rb type NotificationRepo struct { db *gorm.DB } // NewNotificationRepo creates a new Notification repository. func NewNotificationRepo(db *gorm.DB) *NotificationRepo { return &NotificationRepo{db: db} } // FindByID retrieves a notification by primary key. func (r *NotificationRepo) FindByID(ctx context.Context, id uint) (*model.Notification, error) { var n model.Notification err := r.db.WithContext(ctx).First(&n, id).Error if err != nil { return nil, err } return &n, nil } // ListByUser retrieves all notifications for a user with pagination. func (r *NotificationRepo) ListByUser(ctx context.Context, userID uint, offset, limit int) ([]model.Notification, int64, error) { var notifications []model.Notification var total int64 db := r.db.WithContext(ctx).Where("user_id = ?", userID) if err := db.Model(&model.Notification{}).Count(&total).Error; err != nil { return nil, 0, err } if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(¬ifications).Error; err != nil { return nil, 0, err } return notifications, total, nil } // ListByUserAndAccount retrieves notifications for a user within an account. func (r *NotificationRepo) ListByUserAndAccount(ctx context.Context, userID uint, accountID uint, offset, limit int) ([]model.Notification, int64, error) { var notifications []model.Notification var total int64 db := r.db.WithContext(ctx).Where("user_id = ? AND account_id = ?", userID, accountID) if err := db.Model(&model.Notification{}).Count(&total).Error; err != nil { return nil, 0, err } if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(¬ifications).Error; err != nil { return nil, 0, err } return notifications, total, nil } // Create inserts a new notification. func (r *NotificationRepo) Create(ctx context.Context, notification *model.Notification) error { return r.db.WithContext(ctx).Create(notification).Error } // Update modifies an existing notification. func (r *NotificationRepo) Update(ctx context.Context, notification *model.Notification) error { return r.db.WithContext(ctx).Save(notification).Error } // MarkRead marks a single notification as read. func (r *NotificationRepo) MarkRead(ctx context.Context, id uint) error { now := time.Now() result := r.db.WithContext(ctx).Model(&model.Notification{}). Where("id = ?", id). Update("read_at", now) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return gorm.ErrRecordNotFound } return nil } // MarkAllReadByUser marks all unread notifications for a user as read. func (r *NotificationRepo) MarkAllReadByUser(ctx context.Context, userID uint) error { now := time.Now() return r.db.WithContext(ctx).Model(&model.Notification{}). Where("user_id = ? AND read_at IS NULL", userID). Update("read_at", now).Error } // MarkAllReadByUserAndAccount marks all unread notifications for a user within an account as read. func (r *NotificationRepo) MarkAllReadByUserAndAccount(ctx context.Context, userID uint, accountID uint) error { now := time.Now() return r.db.WithContext(ctx).Model(&model.Notification{}). Where("user_id = ? AND account_id = ? AND read_at IS NULL", userID, accountID). Update("read_at", now).Error } // Delete soft-deletes a notification. func (r *NotificationRepo) Delete(ctx context.Context, id uint) error { return r.db.WithContext(ctx).Delete(&model.Notification{}, id).Error } // CountUnreadByUser counts unread notifications for a user. func (r *NotificationRepo) CountUnreadByUser(ctx context.Context, userID uint) (int64, error) { var count int64 err := r.db.WithContext(ctx).Model(&model.Notification{}). Where("user_id = ? AND read_at IS NULL", userID). Count(&count).Error return count, err } // Snooze sets the snoozed_until timestamp and clears read_at for a notification. // Returns the updated notification. // Reference: Chatwoot notifications_controller.rb#snooze func (r *NotificationRepo) Snooze(ctx context.Context, id, userID, accountID uint, snoozedUntil time.Time) (*model.Notification, error) { result := r.db.WithContext(ctx).Model(&model.Notification{}). Where("id = ? AND user_id = ? AND account_id = ?", id, userID, accountID). Updates(map[string]interface{}{"snoozed_until": snoozedUntil, "read_at": nil}) if result.Error != nil { return nil, result.Error } if result.RowsAffected == 0 { return nil, gorm.ErrRecordNotFound } var notif model.Notification if err := r.db.WithContext(ctx).First(¬if, id).Error; err != nil { return nil, err } return ¬if, nil } // MarkUnread clears read_at for a notification, marking it as unread. // Returns the updated notification. // Reference: Chatwoot notifications_controller.rb#unread func (r *NotificationRepo) MarkUnread(ctx context.Context, id, userID, accountID uint) (*model.Notification, error) { result := r.db.WithContext(ctx).Model(&model.Notification{}). Where("id = ? AND user_id = ? AND account_id = ?", id, userID, accountID). Update("read_at", nil) if result.Error != nil { return nil, result.Error } if result.RowsAffected == 0 { return nil, gorm.ErrRecordNotFound } var notif model.Notification if err := r.db.WithContext(ctx).First(¬if, id).Error; err != nil { return nil, err } return ¬if, nil } // DeleteAllByUser soft-deletes all notifications for a user within an account. // Reference: Chatwoot notifications_controller.rb#destroy_all func (r *NotificationRepo) DeleteAllByUser(ctx context.Context, userID, accountID uint) error { return r.db.WithContext(ctx). Where("user_id = ? AND account_id = ?", userID, accountID). Delete(&model.Notification{}).Error }