package httpserver import ( "context" "os" "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/ticket" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" "github.com/robfig/cron/v3" "go.uber.org/zap" ) func StartDataFetchingCrons(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() { mongoLogger.Info("Began fetching upcoming events cron task") if err := eventService.FetchUpcomingEvents(context.Background()); err != nil { mongoLogger.Error("Failed to fetch upcoming events", zap.Error(err), ) } else { mongoLogger.Info("Completed fetching upcoming events without errors") } }, }, { spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events) task: func() { mongoLogger.Info("Began fetching non live odds cron task") if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { mongoLogger.Error("Failed to fetch non live odds", zap.Error(err), ) } else { mongoLogger.Info("Completed fetching non live odds without errors") } }, }, { spec: "0 */5 * * * *", // Every 5 Minutes task: func() { mongoLogger.Info("Began update all expired events status cron task") if _, err := resultService.CheckAndUpdateExpiredB365Events(context.Background()); err != nil { mongoLogger.Error("Failed to update expired events status", zap.Error(err), ) } else { mongoLogger.Info("Completed expired events without errors") } }, }, { spec: "0 */15 * * * *", // Every 15 Minutes task: func() { mongoLogger.Info("Began updating bets based on event results cron task") if err := resultService.FetchB365ResultAndUpdateBets(context.Background()); err != nil { mongoLogger.Error("Failed to process result", zap.Error(err), ) } else { mongoLogger.Info("Completed processing all event result outcomes without errors") } }, }, { spec: "0 0 0 * * 1", // Every Monday task: func() { mongoLogger.Info("Began Send weekly result notification cron task") if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-7*24*time.Hour)); err != nil { mongoLogger.Error("Failed to process result", zap.Error(err), ) } else { mongoLogger.Info("Completed sending weekly result notification without errors") } }, }, } for _, job := range schedule { // job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("Failed to schedule data fetching cron job", zap.Error(err), ) } } c.Start() log.Println("Cron jobs started for event and odds services") mongoLogger.Info("Cron jobs started for event and odds services") } 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("Deleting old tickets") if err := ticketService.DeleteOldTickets(context.Background()); err != nil { mongoLogger.Error("Failed to remove old ticket", zap.Error(err), ) } else { mongoLogger.Info("Successfully deleted old tickets") } }, }, { spec: "0 0 0 * * 1", // Every Monday (Weekly) task: func() { mongoLogger.Info("Deleting old notifications") if err := notificationSvc.DeleteOldNotifications(context.Background()); err != nil { mongoLogger.Error("Failed to remove old notifications", zap.Error(err), ) } else { mongoLogger.Info("Successfully deleted old notifications") } }, }, } for _, job := range schedule { if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("Failed to schedule ticket cron job", zap.Error(err), ) } } c.Start() mongoLogger.Info("Cron jobs started for ticket service") } // SetupReportCronJobs schedules periodic report generation func SetupReportandVirtualGameCronJobs( ctx context.Context, reportService *report.Service, virtualGameOrchestrationService *orchestration.Service, outputDir string, ) { c := cron.New(cron.WithSeconds()) // WithSeconds for testing, remove in prod schedule := []struct { spec string period string }{ // { spec: "*/60 * * * * *", period: "test" }, // every 60 seconds for testing { 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 & provider report job...", period) brandID := os.Getenv("VELI_BRAND_ID") if brandID == "" { log.Println("VELI_BRAND_ID not set, skipping virtual game sync") return } // Step 1. Fetch and store all virtual games req := domain.ProviderRequest{ BrandID: brandID, ExtraData: true, Size: 1000, Page: 1, } allGames, err := virtualGameOrchestrationService.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)) // Step 2. Fetch all providers providers, total, err := virtualGameOrchestrationService.ListProviders(ctx, 1000, 0) if err != nil { log.Printf("[%s] Failed to list providers: %v", period, err) return } else if total == 0 { log.Printf("[%s] No providers found, skipping report generation", period) return } log.Printf("[%s] Found %d total providers", period, total) // Step 3. Create provider-level daily report entries reportDate := time.Now().UTC().Truncate(24 * time.Hour) for _, p := range providers { createReq := domain.CreateVirtualGameProviderReport{ ProviderID: p.ProviderID, ReportDate: reportDate, TotalGamesPlayed: 0, TotalBets: 0, TotalPayouts: 0, TotalPlayers: 0, ReportType: period, // "daily" } _, err := virtualGameOrchestrationService.CreateVirtualGameProviderReport(ctx, createReq) if err != nil { log.Printf("[%s] Failed to create report for provider %s: %v", period, p.ProviderID, err) continue } log.Printf("[%s] Created daily report row for provider: %s", period, p.ProviderID) } log.Printf("[%s] Daily provider reports created successfully", period) }); 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 */2 * * *", // Every 2 hours task: func() { ctx := context.Background() // 1️⃣ Sports mongoLogger.Info("Began fetching and storing sports cron task") if err := enetPulseSvc.FetchAndStoreSports(ctx); err != nil { mongoLogger.Error("Failed to fetch and store sports", zap.Error(err)) } else { mongoLogger.Info("\n\n✅ Completed fetching and storing sports\n\n") } // 2️⃣ Tournament Templates mongoLogger.Info("Began fetching and storing tournament templates cron task") if err := enetPulseSvc.FetchAndStoreTournamentTemplates(ctx); err != nil { mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err)) } else { mongoLogger.Info("\n\n✅ Completed fetching and storing tournament templates\n\n") } // 3️⃣ Tournaments mongoLogger.Info("Began fetching and storing tournaments cron task") if err := enetPulseSvc.FetchAndStoreTournaments(ctx); err != nil { mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err)) } else { mongoLogger.Info("\n\n✅ Completed fetching and storing tournaments\n\n") } // 4️⃣ Tournament Stages // mongoLogger.Info("Began fetching and storing tournament stages cron task") // if err := enetPulseSvc.FetchAndStoreTournamentStages(ctx); err != nil { // mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err)) // } else { // mongoLogger.Info("✅ \n\nCompleted fetching and storing tournament stages\n\n") // } // // 5️⃣ Fixtures mongoLogger.Info("Began fetching and storing fixtures cron task") today := time.Now().Format("2006-01-02") if err := enetPulseSvc.FetchAndStoreFixtures(ctx, today); err != nil { mongoLogger.Error("Failed to fetch and store fixtures", zap.Error(err)) } else { mongoLogger.Info("\n\n✅ Completed fetching and storing fixtures\n\n") } // 6️⃣ Results // mongoLogger.Info("Began fetching and storing results cron task") // if err := enetPulseSvc.FetchAndStoreResults(ctx); err != nil { // mongoLogger.Error("Failed to fetch and store results", zap.Error(err)) // } else { // mongoLogger.Info("\n\n✅ Completed fetching and storing results\n\n") // } // 7 Outcome Types mongoLogger.Info("Began fetching and storing outcome_types cron task") if err := enetPulseSvc.FetchAndStoreOutcomeTypes(ctx); err != nil { mongoLogger.Error("Failed to fetch and store outcome_types", zap.Error(err)) } else { mongoLogger.Info("\n\n✅ Completed fetching and storing outcome_types\n\n") } // 8 Preodds mongoLogger.Info("Began fetching and storing preodds cron task") if err := enetPulseSvc.FetchAndStorePreodds(ctx); err != nil { mongoLogger.Error("Failed to fetch and store preodds", zap.Error(err)) } else { mongoLogger.Info("\n\n✅ Completed fetching and storing preodds\n\n") } }, }, } for _, job := range schedule { // Run 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, tournament stages, fixtures, and results") mongoLogger.Info("EnetPulse cron jobs started for sports, tournament templates, tournaments, tournament stages, fixtures, and results") }