import 'question_source_preferences.dart'; import 'questions_db.dart'; /// Picks the next question source to match configured mix percentages over time. abstract final class QuestionSourceMix { /// Source id with the largest deficit vs configured effective mix (sequential balance). static String pickNextSourceId({ required Map effectiveMixPercent, required Map deliveredCounts, }) { if (effectiveMixPercent.isEmpty) { return 'market_history'; } final int totalDelivered = deliveredCounts.values.fold(0, (int a, int b) => a + b); if (totalDelivered == 0) { return effectiveMixPercent.entries .reduce( ( MapEntry a, MapEntry b, ) => a.value >= b.value ? a : b, ) .key; } String? pick; var maxDeficit = double.negativeInfinity; for (final MapEntry entry in effectiveMixPercent.entries) { final double target = entry.value / 100.0; final double actual = (deliveredCounts[entry.key] ?? 0) / totalDelivered; final double deficit = target - actual; if (deficit > maxDeficit) { maxDeficit = deficit; pick = entry.key; } } return pick ?? effectiveMixPercent.keys.first; } static Map effectiveMixFromResolved( List prefs, ) { return QuestionSourcePreferences.effectiveMix(prefs); } /// Orders unanswered rows so [preferredSourceId] is shown first (stable FIFO within source). static List> orderUnansweredByPreferredSource( List> rows, { required String preferredSourceId, }) { if (rows.length <= 1) { return rows; } final List> preferred = >[]; final List> rest = >[]; for (final Map row in rows) { if (QuestionsDb.resolveSourceId(row) == preferredSourceId) { preferred.add(row); } else { rest.add(row); } } return >[...preferred, ...rest]; } }