package result import ( "context" "encoding/json" "fmt" "strings" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "go.uber.org/zap" ) func (s *Service) CheckAndSendResultNotifications(ctx context.Context, createdAfter time.Time) error { resultLog, err := s.repo.GetAllResultLog(ctx, domain.ResultLogFilter{ CreatedAfter: domain.ValidTime{ Value: createdAfter, Valid: true, }, }) if err != nil { s.mongoLogger.Error( "Failed to get result log", zap.Time("CreatedAfter", createdAfter), zap.Error(err), ) return err } if len(resultLog) == 0 { s.mongoLogger.Info( "No results found for check and send result notification", zap.Time("CreatedAfter", createdAfter), ) return nil } totalResultLog := domain.ResultLog{ StatusNotFinishedCount: resultLog[0].StatusNotFinishedCount, StatusPostponedCount: resultLog[0].StatusPostponedCount, } for _, log := range resultLog { // Add all the bets totalResultLog.StatusNotFinishedBets += log.StatusNotFinishedBets totalResultLog.StatusPostponedBets += log.StatusPostponedBets totalResultLog.StatusToBeFixedBets += log.StatusToBeFixedBets totalResultLog.StatusRemovedBets += log.StatusRemovedBets totalResultLog.StatusEndedBets += log.StatusEndedBets totalResultLog.StatusToBeFixedCount += log.StatusToBeFixedCount totalResultLog.StatusRemovedCount += log.StatusRemovedCount totalResultLog.StatusEndedCount += log.StatusEndedCount totalResultLog.RemovedCount += log.RemovedCount } err = s.SendAdminResultStatusErrorNotification(ctx, totalResultLog, createdAfter, time.Now()) if err != nil { s.mongoLogger.Error( "Failed to send admin result status notification", zap.Time("CreatedAfter", createdAfter), zap.Error(err), ) return err } return nil } func buildHeadlineAndMessage(counts domain.ResultLog, createdAfter time.Time, endTime time.Time) (string, string) { period := fmt.Sprintf("%s - %s", createdAfter.Format("02 Jan 2006"), endTime.Format("02 Jan 2006")) totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets if totalIssues == 0 { return "✅ Successfully Processed Event Results", fmt.Sprintf( "%d total ended events with %d total bets. No issues detected", counts.StatusEndedCount, totalBets, ) } parts := []string{} if counts.StatusNotFinishedCount > 0 { parts = append(parts, fmt.Sprintf("%d unfinished with %d bets", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) } if counts.StatusToBeFixedCount > 0 { parts = append(parts, fmt.Sprintf("%d to-fix with %d bets", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets)) } if counts.StatusPostponedCount > 0 { parts = append(parts, fmt.Sprintf("%d postponed with %d bets", counts.StatusPostponedCount, counts.StatusPostponedBets)) } if counts.StatusRemovedCount > 0 { parts = append(parts, fmt.Sprintf("%d removed with %d bets", counts.StatusRemovedCount, counts.StatusRemovedBets)) } if counts.StatusEndedCount > 0 { parts = append(parts, fmt.Sprintf("%d ended with %d bets", counts.StatusEndedCount, counts.StatusEndedBets)) } headline := "⚠️ Issues Found Processing Event Results" message := fmt.Sprintf("Processed expired event results (%s): %s. Please review pending entries.", period, strings.Join(parts, ", ")) return headline, message } func buildHeadlineAndMessageEmail(counts domain.ResultLog, user domain.User, createdAfter time.Time, endTime time.Time) (string, string, string) { period := fmt.Sprintf("%s - %s", createdAfter.Format("02 Jan 2006"), endTime.Format("02 Jan 2006")) totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount totalEvents := counts.StatusEndedCount + counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets greeting := fmt.Sprintf("Hi %s %s,", user.FirstName, user.LastName) if totalIssues == 0 { headline := "✅ Weekly Results Report — All Events Processed Successfully" plain := fmt.Sprintf(`%s Weekly Results Summary (%s): - %d Ended Events - %d Total Bets All events were processed successfully, and no issues were detected. Best regards, The System`, greeting, period, counts.StatusEndedCount, totalBets) html := fmt.Sprintf(`

%s

Weekly Results Summary

Period: %s

All events were processed successfully, and no issues were detected.

Best regards,
The System

`, greeting, period, counts.StatusEndedCount, totalBets) return headline, plain, html } partsPlain := []string{} partsHTML := []string{} if counts.StatusNotFinishedCount > 0 { partsPlain = append(partsPlain, fmt.Sprintf("- %d Incomplete Events (%d Bets)", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) partsHTML = append(partsHTML, fmt.Sprintf("
  • %d Incomplete Events (%d Bets)
  • ", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) } if counts.StatusToBeFixedCount > 0 { partsPlain = append(partsPlain, fmt.Sprintf("- %d Requires Review (%d Bets)", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets)) partsHTML = append(partsHTML, fmt.Sprintf("
  • %d Requires Review (%d Bets)
  • ", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets)) } if counts.StatusPostponedCount > 0 { partsPlain = append(partsPlain, fmt.Sprintf("- %d Postponed Events (%d Bets)", counts.StatusPostponedCount, counts.StatusPostponedBets)) partsHTML = append(partsHTML, fmt.Sprintf("
  • %d Postponed Events (%d Bets)
  • ", counts.StatusPostponedCount, counts.StatusPostponedBets)) } if counts.StatusRemovedCount > 0 { partsPlain = append(partsPlain, fmt.Sprintf("- %d Discarded Events (%d Bets)", counts.StatusRemovedCount, counts.StatusRemovedBets)) partsHTML = append(partsHTML, fmt.Sprintf("
  • %d Discarded Events (%d Bets)
  • ", counts.StatusRemovedCount, counts.StatusRemovedBets)) } if counts.StatusEndedCount > 0 { partsPlain = append(partsPlain, fmt.Sprintf("- %d Successfully Ended Events (%d Bets)", counts.StatusEndedCount, counts.StatusEndedBets)) partsHTML = append(partsHTML, fmt.Sprintf("
  • %d Successfully Ended Events (%d Bets)
  • ", counts.StatusEndedCount, counts.StatusEndedBets)) } headline := "⚠️ Weekly Results Report — Review Required" plain := fmt.Sprintf(`%s Weekly Results Summary (%s): %s Totals: - %d Events Processed - %d Total Bets Next Steps: Some events require your attention. Please log into the admin dashboard to review pending issues. Best regards, The System`, greeting, period, strings.Join(partsPlain, "\n"), totalEvents, totalBets, ) html := fmt.Sprintf(`

    %s

    Weekly Results Summary

    Period: %s

    Totals

    Next Steps:
    Some events require your attention. Please log into the admin dashboard to review pending issues.

    Best regards,
    The System

    `, greeting, period, strings.Join(partsHTML, "\n"), totalEvents, totalBets, ) return headline, plain, html } func (s *Service) SendAdminResultStatusErrorNotification( ctx context.Context, counts domain.ResultLog, createdAfter time.Time, endTime time.Time, ) error { superAdmins, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ Role: string(domain.RoleSuperAdmin), }) if err != nil { s.mongoLogger.Error("failed to get super_admin recipients", zap.Error(err)) return err } metaBytes, err := json.Marshal(counts) if err != nil { s.mongoLogger.Error("failed to marshal metadata", zap.Error(err)) return err } headline, message := buildHeadlineAndMessage(counts, createdAfter, endTime) notification := &domain.Notification{ ErrorSeverity: domain.NotificationErrorSeverityHigh, DeliveryStatus: domain.DeliveryStatusPending, IsRead: false, Type: domain.NOTIFICATION_TYPE_BET_RESULT, Level: domain.NotificationLevelWarning, Reciever: domain.NotificationRecieverSideAdmin, DeliveryChannel: domain.DeliveryChannelInApp, Payload: domain.NotificationPayload{ Headline: headline, Message: message, }, Priority: 2, Metadata: metaBytes, } var sendErrors []error for _, user := range superAdmins { notification.RecipientID = user.ID if err := s.notificationSvc.SendNotification(ctx, notification); err != nil { s.mongoLogger.Error("failed to send admin notification", zap.Int64("admin_id", user.ID), zap.Error(err), ) sendErrors = append(sendErrors, err) } // notification.DeliveryChannel = domain.DeliveryChannelEmail if user.Email == "" { continue } subject, plain, html := buildHeadlineAndMessageEmail(counts, user, createdAfter, endTime) if err := s.messengerSvc.SendEmail(ctx, user.Email, plain, html, subject); err != nil { s.mongoLogger.Error("failed to send admin result report email", zap.Int64("admin_id", user.ID), zap.Error(err), ) sendErrors = append(sendErrors, err) } } if len(sendErrors) > 0 { return fmt.Errorf("sent with partial failure: %d errors", len(sendErrors)) } return nil }