import type { RecurrenceRule } from "@prisma/client";

const clampDay = (year: number, monthIndex: number, day: number) => {
  const lastDay = new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
  return Math.min(day, lastDay);
};

export const addMonthsWithClamp = (date: Date, months: number) => {
  const year = date.getUTCFullYear();
  const month = date.getUTCMonth();
  const day = date.getUTCDate();
  const hour = date.getUTCHours();
  const minute = date.getUTCMinutes();
  const second = date.getUTCSeconds();

  const targetMonth = month + months;
  const targetYear = year + Math.floor(targetMonth / 12);
  const normalizedMonth = ((targetMonth % 12) + 12) % 12;
  const clampedDay = clampDay(targetYear, normalizedMonth, day);

  return new Date(Date.UTC(targetYear, normalizedMonth, clampedDay, hour, minute, second));
};

export const addYearsWithClamp = (date: Date, years: number) => {
  const targetYear = date.getUTCFullYear() + years;
  const month = date.getUTCMonth();
  const day = date.getUTCDate();
  const hour = date.getUTCHours();
  const minute = date.getUTCMinutes();
  const second = date.getUTCSeconds();
  const clampedDay = clampDay(targetYear, month, day);
  return new Date(Date.UTC(targetYear, month, clampedDay, hour, minute, second));
};

export const nextDueAt = (dueAt: Date, rule: RecurrenceRule) => {
  const interval = rule.interval ?? 1;
  switch (rule.type) {
    case "hourly":
      return new Date(dueAt.getTime() + interval * 60 * 60 * 1000);
    case "daily":
      return new Date(dueAt.getTime() + interval * 24 * 60 * 60 * 1000);
    case "weekly":
      return new Date(dueAt.getTime() + interval * 7 * 24 * 60 * 60 * 1000);
    case "monthly":
      return addMonthsWithClamp(dueAt, interval);
    case "yearly":
      return addYearsWithClamp(dueAt, interval);
    default:
      return dueAt;
  }
};
