package httpserver import ( "context" "os" "time" // "time" "log" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse" eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/report" resultsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/stats" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" "github.com/robfig/cron/v3" "go.uber.org/zap" ) func StartBetAPIDataFetchingCrons( eventService *eventsvc.Service, oddsService oddssvc.ServiceImpl, resultService *resultsvc.Service, mongoLogger *zap.Logger, ) { c := cron.New(cron.WithSeconds()) schedule := []struct { spec string task func() }{ { spec: "0 0 * * * *", // Every 1 hour task: func() { start := time.Now() mongoLogger.Info("[BetAPI Event Fetching Crons] Began fetching upcoming events cron task", zap.Time("timestamp", time.Now())) if err := eventService.FetchUpcomingEvents(context.Background()); err != nil { mongoLogger.Error("[BetAPI Event Fetching Crons] Failed to fetch upcoming events", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), zap.Error(err), ) } else { mongoLogger.Info("[BetAPI Event Fetching Crons] Completed fetching upcoming events without errors", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } }, }, { spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events) task: func() { start := time.Now() mongoLogger.Info("[BetAPI Pre-Match Odds Fetching Crons] Began fetching pre-match odds cron task", zap.Time("timestamp", time.Now()), ) if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { mongoLogger.Error("[BetAPI Pre-Match Odds Fetching Crons] Failed to fetch pre-match odds", zap.Error(err), zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } else { mongoLogger.Info("[BetAPI Pre-Match Odds Fetching Crons] Completed fetching pre-match odds without errors", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } }, }, { spec: "0 */5 * * * *", // Every 5 Minutes task: func() { start := time.Now() mongoLogger.Info("[BetAPI Check And Update Expired Events Crons] Began update all expired events status cron task", zap.Time("timestamp", time.Now()), ) if _, err := resultService.CheckAndUpdateExpiredB365Events(context.Background()); err != nil { mongoLogger.Error("[BetAPI Check And Update Expired Events Crons] Failed to update expired events status", zap.Error(err), zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } else { mongoLogger.Info("[BetAPI Check And Update Expired Events Crons] Completed expired events without errors", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } }, }, { spec: "0 */15 * * * *", // Every 15 Minutes task: func() { start := time.Now() mongoLogger.Info("[BetAPI Fetch Result and Update Bets Crons] Began updating bets based on event results cron task") if err := resultService.FetchB365ResultAndUpdateBets(context.Background()); err != nil { mongoLogger.Error("[BetAPI Fetch Result and Update Bets Crons] Failed to process result", zap.Error(err), zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } else { mongoLogger.Info("[BetAPI Fetch Result and Update Bets Crons] Completed processing all event result outcomes without errors", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } }, }, { spec: "0 0 0 * * 1", // Every Monday task: func() { mongoLogger.Info("[BetAPI Send Result Notification Crons] Began Send weekly result notification cron task", zap.Time("timestamp", time.Now()), ) if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-7*24*time.Hour)); err != nil { mongoLogger.Error("[BetAPI Send Result Notification Crons] Failed to process result", zap.Time("timestamp", time.Now()), zap.Error(err), ) } else { mongoLogger.Info("[BetAPI Send Result Notification Crons] Completed sending weekly result notification without errors", zap.Time("timestamp", time.Now()), ) } }, }, } for _, job := range schedule { // job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("[BetAPI Data Fetching Crons] Failed to schedule data fetching cron job", zap.Time("timestamp", time.Now()), zap.Error(err), ) } } c.Start() mongoLogger.Info("[BetAPI Data Fetching Crons] Started Data Fetching Cron jobs", zap.Time("timestamp", time.Now()), ) } func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) { c := cron.New(cron.WithSeconds()) schedule := []struct { spec string task func() }{ { spec: "0 0 * * * *", // Every hour task: func() { mongoLogger.Info("[Delete Old Ticket Crons] Deleting old tickets", zap.Time("timestamp", time.Now())) if err := ticketService.DeleteOldTickets(context.Background()); err != nil { mongoLogger.Error("[Delete Old Ticket Crons] Failed to remove old ticket", zap.Error(err), zap.Time("timestamp", time.Now()), ) } else { mongoLogger.Info("[Delete Old Ticket Crons] Successfully deleted old tickets", zap.Time("timestamp", time.Now()), ) } }, }, { spec: "0 0 0 * * 1", // Every Monday (Weekly) task: func() { mongoLogger.Info("[Delete Old Notification Crons] Deleting old notifications") if err := notificationSvc.DeleteOldNotifications(context.Background()); err != nil { mongoLogger.Error("Failed to remove old notifications", zap.Time("timestamp", time.Now()), zap.Error(err), ) } else { mongoLogger.Info("[Delete Old Notification Crons] Successfully deleted old notifications", zap.Time("timestamp", time.Now()), ) } }, }, } for _, job := range schedule { if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("[Start Cleanup Crons] Failed to schedule cleanup cron job", zap.Time("timestamp", time.Now()), zap.Error(err), ) } } c.Start() mongoLogger.Info("[Start Cleanup Crons] Started cleanup cron jobs", zap.Time("timestamp", time.Now()), ) } func StartStatCrons(statService *stats.Service, mongoLogger *zap.Logger) { c := cron.New(cron.WithSeconds()) schedule := []struct { spec string task func() }{ { spec: "0 0 * * * *", // Every hour task: func() { start := time.Now() mongoLogger.Info("[Company Stats Crons] Updating company stats", zap.Time("timestamp", time.Now())) if err := statService.UpdateCompanyStats(context.Background()); err != nil { mongoLogger.Error("[Company Stats Crons] Failed to update company stats", zap.Error(err), zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } else { mongoLogger.Info("[Company Stats Crons] Successfully updated company stats", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } }, }, { spec: "0 0 * * * *", // Every hour task: func() { start := time.Now() mongoLogger.Info("[Branch Stats Crons] Updating branch stats", zap.Time("timestamp", time.Now())) if err := statService.UpdateBranchStats(context.Background()); err != nil { mongoLogger.Error("[Branch Stats Crons] Failed to update branch stats", zap.Error(err), zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } else { mongoLogger.Info("[Branch Stats Crons] Successfully updated branch stats", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } }, }, { spec: "0 0 * * * *", // Every hour task: func() { start := time.Now() mongoLogger.Info("[Wallet Stats Crons] Updating wallet stats", zap.Time("timestamp", time.Now())) if err := statService.UpdateWalletStats(context.Background()); err != nil { mongoLogger.Error("[Wallet Stats Crons] Failed to update wallet stats", zap.Error(err), zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } else { mongoLogger.Info("[Wallet Stats Crons] Successfully updated wallet stats", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } }, }, { spec: "0 0 * * * *", // Hourly task: func() { start := time.Now() mongoLogger.Info("[Event Stats Crons] Updating event stats") if err := statService.UpdateEventBetStats(context.Background()); err != nil { mongoLogger.Error("[Event Stats Crons] Failed to update event bet stats", zap.Error(err), zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start)), ) } else { mongoLogger.Info("[Event Stats Crons] Successfully updated event stats", zap.Time("timestamp", time.Now()), zap.Duration("duration", time.Since(start))) } }, }, } for _, job := range schedule { job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("[Starting Stats Crons] Failed to schedule stats cron job", zap.Error(err), zap.Time("timestamp", time.Now()), ) } } c.Start() mongoLogger.Info("Starting Stats Crons] Started Stat Cron Jobs", zap.Time("timestamp", time.Now())) } func StartReportCrons(reportService report.ReportService, mongoLogger *zap.Logger) { c := cron.New(cron.WithSeconds()) schedule := []struct { spec string task func() }{ { spec: "0 * * * * *", // Every 5 Minutes task: func() { mongoLogger.Info("[Process Report Crons] Started Checking and Processing Reports") if err := reportService.ProcessReportRequests(context.Background()); err != nil { mongoLogger.Error("[Process Report Crons] Failed to process reports", zap.Error(err), ) } else { mongoLogger.Info("[Process Report Crons] Successfully processed all reports") } }, }, } for _, job := range schedule { job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("[Report Crons] Failed to schedule report cron job", zap.Time("timestamp", time.Now()), zap.Error(err), ) } } c.Start() mongoLogger.Info("[Report Crons] Cron jobs started for reports", zap.Time("timestamp", time.Now())) } // SetupReportCronJobs schedules periodic report generation func SetupReportandVirtualGameCronJobs( ctx context.Context, reportService report.ReportService, virtualGameService *veli.Service, // inject your virtual game service outputDir string, ) { c := cron.New(cron.WithSeconds()) // use WithSeconds for testing schedule := []struct { spec string period string }{ // { // spec: "*/60 * * * * *", // Every 1 minute for testing // period: "test", // }, { spec: "0 0 0 * * *", // Daily at midnight period: "daily", }, } for _, job := range schedule { period := job.period if _, err := c.AddFunc(job.spec, func() { log.Printf("[%s] Running virtual game fetch & store job...", period) brandID := os.Getenv("VELI_BRAND_ID") if brandID == "" { log.Println("VELI_BRAND_ID not set, skipping virtual game sync") return } req := domain.ProviderRequest{ BrandID: brandID, ExtraData: true, Size: 1000, Page: 1, } allGames, err := virtualGameService.FetchAndStoreAllVirtualGames(ctx, req, "ETB") if err != nil { log.Printf("[%s] Error fetching/storing virtual games: %v", period, err) return } log.Printf("[%s] Successfully fetched & stored %d virtual games", period, len(allGames)) // --- Generate reports only for daily runs --- // if period == "daily" { // now := time.Now() // from := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location()) // to := time.Date(now.Year(), now.Month(), now.Day()-1, 23, 59, 59, 0, now.Location()) // log.Printf("Running daily report for period %s -> %s", from.Format(time.RFC3339), to.Format(time.RFC3339)) // if err := reportService.GenerateReport(ctx, from, to); err != nil { // log.Printf("Error generating daily report: %v", err) // } else { // log.Printf("Successfully generated daily report") // } // } }); err != nil { log.Fatalf("Failed to schedule %s cron job: %v", period, err) } } c.Start() log.Printf("Cron jobs started. Reports will be saved to: %s", outputDir) } func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) { c := cron.New(cron.WithSeconds()) schedule := []struct { spec string task func() }{ { spec: "0 0 0 * * *", // Daily at midnight task: func() { log.Println("process bet cashbacks...") if err := betService.ProcessBetCashback(ctx); err != nil { log.Printf("Failed to process bet cashbacks: %v", err) } else { log.Printf("Successfully processed bet cashbacks") } }, }, } for _, job := range schedule { if _, err := c.AddFunc(job.spec, job.task); err != nil { log.Fatalf("Failed to schedule cron job: %v", err) } } c.Start() log.Println("Cron jobs started for bet cashbacks") } func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger) { c := cron.New(cron.WithSeconds()) schedule := []struct { spec string task func() }{ { spec: "0 0,10,20,30,40,50 * * * *", // Every 10 minutes task: func() { mongoLogger.Info("Began fetching and storing sports cron task") if err := enetPulseSvc.FetchAndStoreSports(context.Background()); err != nil { mongoLogger.Error("Failed to fetch and store sports", zap.Error(err), ) } else { mongoLogger.Info("Completed fetching and storing sports without errors") } mongoLogger.Info("Began fetching and storing tournament templates cron task") if err := enetPulseSvc.FetchAndStoreTournamentTemplates(context.Background()); err != nil { mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err), ) } else { mongoLogger.Info("Completed fetching and storing tournament templates without errors") } mongoLogger.Info("Began fetching and storing tournaments cron task") if err := enetPulseSvc.FetchAndStoreTournaments(context.Background()); err != nil { mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err), ) } else { mongoLogger.Info("Completed fetching and storing tournaments without errors") } mongoLogger.Info("Began fetching and storing tournament stages cron task") if err := enetPulseSvc.FetchAndStoreTournamentStages(context.Background()); err != nil { mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err), ) } else { mongoLogger.Info("Completed fetching and storing tournament stages without errors") } }, }, } for _, job := range schedule { // Run the task immediately at startup job.task() // Schedule the task if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("Failed to schedule EnetPulse cron job", zap.Error(err), ) } } c.Start() log.Println("EnetPulse cron jobs started for sports, tournament templates, tournaments, and tournament stages") mongoLogger.Info("EnetPulse cron jobs started for sports, tournament templates, tournaments, and tournament stages") }