/// Daily streak: at least one stage attempt (win or lose) per local
/// calendar day keeps it alive. No clock-cheat defense — single-player,
/// low stakes.
class StreakState {
  const StreakState({
    required this.current,
    required this.best,
    required this.lastYmd,
    this.hitMilestone,
  });

  static const initial = StreakState(current: 0, best: 0, lastYmd: null);

  static const milestones = [3, 7, 14, 30];

  final int current;
  final int best;

  /// Local date of the last counted play, as `yyyy-MM-dd`.
  final String? lastYmd;

  /// Set when this advance just reached a milestone (celebrate once).
  final int? hitMilestone;
}

String _ymd(DateTime d) =>
    '${d.year.toString().padLeft(4, '0')}-'
    '${d.month.toString().padLeft(2, '0')}-'
    '${d.day.toString().padLeft(2, '0')}';

StreakState advanceStreak(StreakState state, DateTime now) {
  final today = _ymd(now);
  if (state.lastYmd == today) {
    return StreakState(
      current: state.current,
      best: state.best,
      lastYmd: today,
    );
  }
  // Normalized constructor handles month/year boundaries (and DST).
  final yesterday = _ymd(DateTime(now.year, now.month, now.day - 1));
  final current = state.lastYmd == yesterday ? state.current + 1 : 1;
  return StreakState(
    current: current,
    best: current > state.best ? current : state.best,
    lastYmd: today,
    hitMilestone: StreakState.milestones.contains(current) ? current : null,
  );
}
