package wallet import ( "context" "encoding/json" "fmt" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "go.uber.org/zap" ) func (s *Service) GetAdminNotificationRecipients(ctx context.Context, walletID int64, walletType domain.WalletType) ([]int64, error) { var recipients []int64 switch walletType { case domain.BranchWalletType: branch, err := s.GetBranchByWalletID(ctx, walletID) if err != nil { s.mongoLogger.Error("[GetAdminNotificationRecipients] failed to GetBranchWalletByID", zap.Int64("walletID", walletID)) return nil, err } // Branch managers will be notified when branch wallet is empty recipients = append(recipients, branch.BranchManagerID) // Cashier will be notified cashiers, err := s.userSvc.GetCashiersByBranch(ctx, branch.ID) if err != nil { return nil, err } for _, cashier := range cashiers { recipients = append(recipients, cashier.ID) } // Admin will also be notified admin, err := s.userSvc.GetAdminByCompanyID(ctx, branch.CompanyID) if err != nil { return nil, err } recipients = append(recipients, admin.ID) case domain.CompanyWalletType: company, err := s.GetCompanyByWalletID(ctx, walletID) if err != nil { return nil, err } recipients = append(recipients, company.AdminID) default: return nil, fmt.Errorf("Invalid wallet type") } users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ Role: string(domain.RoleSuperAdmin), }) if err != nil { return nil, err } for _, user := range users { recipients = append(recipients, user.ID) } return recipients, nil } func (s *Service) SendWalletUpdateNotification(ctx context.Context, wallet domain.Wallet) error { raw, _ := json.Marshal(map[string]any{ "balance": wallet.Balance.Float32(), "type": wallet.Type, "timestamp": time.Now(), }) headline := "" message := "" var receiver domain.NotificationRecieverSide switch wallet.Type { case domain.StaticWalletType: headline = "Referral and Bonus Wallet Updated" message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", wallet.Balance.Float32()) receiver = domain.NotificationRecieverSideCustomer case domain.RegularWalletType: headline = "Wallet Updated" message = fmt.Sprintf("Your wallet balance is now %.2f", wallet.Balance.Float32()) receiver = domain.NotificationRecieverSideCustomer case domain.BranchWalletType: headline = "Branch Wallet Updated" message = fmt.Sprintf("branch wallet balance is now %.2f", wallet.Balance.Float32()) receiver = domain.NotificationRecieverSideBranchManager case domain.CompanyWalletType: headline = "Company Wallet Updated" message = fmt.Sprintf("company wallet balance is now %.2f", wallet.Balance.Float32()) receiver = domain.NotificationRecieverSideAdmin } // Handle the wallet event: send notification notification := &domain.Notification{ RecipientID: wallet.UserID, DeliveryChannel: domain.DeliveryChannelInApp, Reciever: receiver, Type: domain.NotificationTypeWalletUpdated, DeliveryStatus: domain.DeliveryStatusPending, IsRead: false, Level: domain.NotificationLevelInfo, Priority: 2, Metadata: raw, Payload: domain.NotificationPayload{ Headline: headline, Message: message, }, } if err := s.notificationSvc.SendNotification(ctx, notification); err != nil { s.mongoLogger.Error("[WalletSvc.SendWalletUpdateNotification] Failed to send notification", zap.Error(err), zap.Time("timestamp", time.Now()), ) return err } return nil } func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet) error { // Send different messages // Send notification to admin team adminNotification := &domain.Notification{ ErrorSeverity: "low", IsRead: false, DeliveryStatus: domain.DeliveryStatusPending, RecipientID: adminWallet.UserID, Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT, Level: domain.NotificationLevelWarning, Reciever: domain.NotificationRecieverSideAdmin, DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel Payload: domain.NotificationPayload{ Headline: "CREDIT WARNING: System Running Out of Funds", Message: fmt.Sprintf( "Wallet ID %d is running low. Current balance: %.2f", adminWallet.ID, adminWallet.Balance.Float32(), ), }, Priority: 1, // High priority for admin alerts Metadata: fmt.Appendf(nil, `{ "wallet_id": %d, "balance": %d, "notification_type": "admin_alert" }`, adminWallet.ID, adminWallet.Balance), } // Get admin recipients and send to all adminRecipients, err := s.GetAdminNotificationRecipients(ctx, adminWallet.ID, adminWallet.Type) if err != nil { s.mongoLogger.Error("failed to get admin recipients", zap.Error(err), zap.Time("timestamp", time.Now()), ) return err } for _, adminID := range adminRecipients { adminNotification.RecipientID = adminID if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { s.mongoLogger.Error("failed to send admin notification", zap.Int64("admin_id", adminID), zap.Error(err), zap.Time("timestamp", time.Now()), ) } // adminNotification.DeliveryChannel = domain.DeliveryChannelEmail // if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { // s.mongoLogger.Error("failed to send email admin notification", // zap.Int64("admin_id", adminID), // zap.Error(err), // zap.Time("timestamp", time.Now()), // ) // return err // } } return nil } func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, adminWallet domain.Wallet, amount domain.Currency) error { // Send notification to admin team adminNotification := &domain.Notification{ ErrorSeverity: domain.NotificationErrorSeverityLow, IsRead: false, DeliveryStatus: domain.DeliveryStatusPending, RecipientID: adminWallet.UserID, Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT, Level: domain.NotificationLevelError, Reciever: domain.NotificationRecieverSideAdmin, DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel Payload: domain.NotificationPayload{ Headline: "CREDIT Error: Admin Wallet insufficient to process customer request", Message: fmt.Sprintf( "Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f", adminWallet.ID, amount.Float32(), adminWallet.Balance.Float32(), ), }, Priority: 1, // High priority for admin alerts Metadata: fmt.Appendf(nil, `{ "wallet_id": %d, "balance": %d, "transaction amount": %.2f, "notification_type": "admin_alert" }`, adminWallet.ID, adminWallet.Balance, amount.Float32()), } // Get admin recipients and send to all recipients, err := s.GetAdminNotificationRecipients(ctx, adminWallet.ID, adminWallet.Type) if err != nil { s.mongoLogger.Error("failed to get admin recipients", zap.Error(err), zap.Time("timestamp", time.Now()), ) return err } for _, adminID := range recipients { adminNotification.RecipientID = adminID if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { s.mongoLogger.Error("failed to send admin notification", zap.Int64("admin_id", adminID), zap.Error(err), zap.Time("timestamp", time.Now()), ) } adminNotification.DeliveryChannel = domain.DeliveryChannelEmail if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { s.mongoLogger.Error("failed to send email admin notification", zap.Int64("admin_id", adminID), zap.Error(err), zap.Time("timestamp", time.Now()), ) return err } } return nil } func (s *Service) SendCustomerWalletInsufficientNotification(ctx context.Context, customerWallet domain.Wallet, amount domain.Currency) error { // Send notification to admin team customerNotification := &domain.Notification{ ErrorSeverity: domain.NotificationErrorSeverityLow, IsRead: false, DeliveryStatus: domain.DeliveryStatusPending, RecipientID: customerWallet.UserID, Type: domain.NOTIFICATION_TYPE_WALLET, Level: domain.NotificationLevelError, Reciever: domain.NotificationRecieverSideCustomer, DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel Payload: domain.NotificationPayload{ Headline: "CREDIT Error: Wallet insufficient", Message: fmt.Sprintf( "Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f", customerWallet.ID, amount.Float32(), customerWallet.Balance.Float32(), ), }, Priority: 1, // High priority for admin alerts Metadata: fmt.Appendf(nil, `{ "wallet_id": %d, "balance": %d, "transaction amount": %.2f, "notification_type": "admin_alert" }`, customerWallet.ID, customerWallet.Balance, amount.Float32()), } if err := s.notificationSvc.SendNotification(ctx, customerNotification); err != nil { s.mongoLogger.Error("failed to create customer notification", zap.Int64("customer_id", customerWallet.UserID), zap.Error(err), zap.Time("timestamp", time.Now()), ) return err } return nil }