package scheduler import ( "fmt" "time" "veola/internal/models" "veola/templates" ) // PickBadge returns the highest-priority deal-quality badge that applies to // an item, or an empty BadgeData if none match. Order: // 1. All-time low // 2. X% below 30-day avg (only when at least 10% below) // 3. X% below target func PickBadge(it models.Item, history []models.PricePoint, now time.Time) templates.BadgeData { if it.BestPrice == nil { return templates.BadgeData{} } best := *it.BestPrice // 1. All-time low if isAllTimeLow(best, history) { return templates.BadgeData{Label: "All-time low", Class: "v-badge-low"} } // 2. X% below 30-day average if avg, ok := windowedMean(history, now, 30*24*time.Hour); ok && best > 0 && avg > 0 { pct := (avg - best) / avg * 100 if pct >= 10 { return templates.BadgeData{ Label: fmt.Sprintf("%d%% below 30-day avg", int(pct+0.5)), Class: "v-badge-avg", } } } // 3. X% below target if it.TargetPrice != nil && *it.TargetPrice > 0 && best < *it.TargetPrice { pct := (*it.TargetPrice - best) / *it.TargetPrice * 100 return templates.BadgeData{ Label: fmt.Sprintf("%d%% below target", int(pct+0.5)), Class: "v-badge-target", } } return templates.BadgeData{} } func isAllTimeLow(best float64, history []models.PricePoint) bool { if len(history) == 0 { return false } for _, p := range history { if p.Price > 0 && p.Price < best { return false } } return true } func windowedMean(history []models.PricePoint, now time.Time, window time.Duration) (float64, bool) { cutoff := now.Add(-window) sum, n := 0.0, 0 for _, p := range history { if p.PolledAt.Before(cutoff) { continue } sum += p.Price n++ } if n == 0 { return 0, false } return sum / float64(n), true }