import 'frequency.dart';

/// Calculate the next due date from the current due date and interval config.
///
/// For calendar-anchored intervals, [anchorDay] is the original day-of-month.
/// Day-count intervals use pure arithmetic (Duration).
/// Calendar-anchored intervals use month arithmetic with clamping.
DateTime calculateNextDueDate({
  required DateTime currentDueDate,
  required IntervalType intervalType,
  required int intervalDays,
  int? anchorDay,
}) {
  switch (intervalType) {
    // Day-count: pure arithmetic
    case IntervalType.daily:
      return currentDueDate.add(const Duration(days: 1));
    case IntervalType.everyNDays:
      return currentDueDate.add(Duration(days: intervalDays));
    case IntervalType.weekly:
      return currentDueDate.add(const Duration(days: 7));
    case IntervalType.biweekly:
      return currentDueDate.add(const Duration(days: 14));
    // Calendar-anchored: month arithmetic with clamping
    case IntervalType.monthly:
      return _addMonths(currentDueDate, 1, anchorDay);
    case IntervalType.everyNMonths:
      return _addMonths(currentDueDate, intervalDays, anchorDay);
    case IntervalType.quarterly:
      return _addMonths(currentDueDate, 3, anchorDay);
    case IntervalType.yearly:
      return _addMonths(currentDueDate, 12, anchorDay);
  }
}

/// Add months with day-of-month clamping.
/// [anchorDay] remembers the original day for correct clamping.
DateTime _addMonths(DateTime date, int months, int? anchorDay) {
  final targetMonth = date.month + months;
  final targetYear = date.year + (targetMonth - 1) ~/ 12;
  final normalizedMonth = ((targetMonth - 1) % 12) + 1;
  final day = anchorDay ?? date.day;
  // Last day of target month: day 0 of next month
  final lastDay = DateTime(targetYear, normalizedMonth + 1, 0).day;
  final clampedDay = day > lastDay ? lastDay : day;
  return DateTime(targetYear, normalizedMonth, clampedDay);
}

/// Catch-up: if next due is in the past, keep adding intervals until future/today.
DateTime catchUpToPresent({
  required DateTime nextDue,
  required DateTime today,
  required IntervalType intervalType,
  required int intervalDays,
  int? anchorDay,
}) {
  while (nextDue.isBefore(today)) {
    nextDue = calculateNextDueDate(
      currentDueDate: nextDue,
      intervalType: intervalType,
      intervalDays: intervalDays,
      anchorDay: anchorDay,
    );
  }
  return nextDue;
}
