package services import ( "database/sql" "fmt" "log" "synlotto-website/handlers" "synlotto-website/helpers" "synlotto-website/models" "synlotto-website/rules" draws "synlotto-website/services/draws" ) func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, error) { stats := models.MatchRunStats{} rows, err := db.Query(` SELECT id, game_type, draw_date, ball1, ball2, ball3, ball4, ball5, ball6, bonus1, bonus2 FROM my_tickets WHERE matched_main IS NULL `) if err != nil { return stats, err } defer rows.Close() // Buffer results to avoid writing while iterating var pending []models.Ticket for rows.Next() { var t models.Ticket var b1, b2, b3, b4, b5, b6, bo1, bo2 sql.NullInt64 if err := rows.Scan( &t.Id, &t.GameType, &t.DrawDate, &b1, &b2, &b3, &b4, &b5, &b6, &bo1, &bo2, ); err != nil { continue } t.Ball1 = int(b1.Int64) t.Ball2 = int(b2.Int64) t.Ball3 = int(b3.Int64) t.Ball4 = int(b4.Int64) t.Ball5 = int(b5.Int64) t.Ball6 = int(b6.Int64) t.Bonus1 = helpers.IntPtrIfValid(bo1) t.Bonus2 = helpers.IntPtrIfValid(bo2) pending = append(pending, t) } for _, t := range pending { matchTicket := models.MatchTicket{ ID: t.Id, GameType: t.GameType, DrawDate: t.DrawDate, Balls: helpers.BuildBallsSlice(t), BonusBalls: helpers.BuildBonusSlice(t), } draw := draws.GetDrawResultForTicket(db, t.GameType, t.DrawDate) result := handlers.MatchTicketToDraw(matchTicket, draw, rules.ThunderballPrizeRules) if result.MatchedDrawID == 0 { continue } _, err := db.Exec(` UPDATE my_tickets SET matched_main = ?, matched_bonus = ?, prize_tier = ?, is_winner = ? WHERE id = ? `, result.MatchedMain, result.MatchedBonus, result.PrizeTier, result.IsWinner, t.Id) if err != nil { log.Println("⚠️ Failed to update ticket match:", err) continue } stats.TicketsMatched++ if result.IsWinner { stats.WinnersFound++ } } _, _ = db.Exec(` INSERT INTO log_ticket_matching (triggered_by, tickets_matched, winners_found) VALUES (?, ?, ?) `, triggeredBy, stats.TicketsMatched, stats.WinnersFound) return stats, nil } func UpdateMissingPrizes(db *sql.DB) error { type TicketInfo struct { ID int GameType string DrawDate string Main int Bonus int } var tickets []TicketInfo // Step 1: Load all relevant tickets rows, err := db.Query(` SELECT id, game_type, draw_date, matched_main, matched_bonus FROM my_tickets WHERE is_winner = 1 AND (prize_label IS NULL OR prize_label = '') `) if err != nil { return err } defer rows.Close() for rows.Next() { var t TicketInfo if err := rows.Scan(&t.ID, &t.GameType, &t.DrawDate, &t.Main, &t.Bonus); err != nil { log.Println("⚠️ Failed to scan row:", err) continue } tickets = append(tickets, t) } // Step 2: Now that the reader is closed, perform updates for _, t := range tickets { if t.GameType != "Thunderball" { continue } idx, ok := rules.GetThunderballPrizeIndex(t.Main, t.Bonus) if !ok { log.Printf("❌ No index for %d main, %d bonus", t.Main, t.Bonus) continue } query := fmt.Sprintf(`SELECT prize%d_per_winner FROM prizes_thunderball WHERE draw_date = ?`, idx) var amount int err := db.QueryRow(query, t.DrawDate).Scan(&amount) if err != nil { log.Printf("❌ Prize lookup failed for ticket %d: %v", t.ID, err) continue } label := "Free Ticket" if amount > 0 { label = fmt.Sprintf("£%.2f", float64(amount)) } _, err = db.Exec(` UPDATE my_tickets SET prize_amount = ?, prize_label = ? WHERE id = ? `, float64(amount), label, t.ID) if err != nil { log.Printf("❌ Failed to update ticket %d: %v", t.ID, err) } else { log.Printf("✅ Updated ticket %d → %s", t.ID, label) } } return nil }