const pad = (value: number): string => String(value).padStart(2, '0');

const formatDateTime = (date: Date): string =>
  `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;

const normalizeDate = (value: string | Date): Date => {
  const date = value instanceof Date ? new Date(value) : new Date(String(value).replace(/-/g, '/'));
  return Number.isNaN(date.getTime()) ? new Date() : date;
};

const iterateDates = (start: Date, end: Date, stepper: (date: Date) => void): string[] => {
  const result: string[] = [];
  const cursor = new Date(start);

  while (cursor.getTime() <= end.getTime()) {
    result.push(formatDateTime(cursor));
    stepper(cursor);
  }

  return result;
};

export const getHoursBetween = (startTime: string | Date, endTime: string | Date): string[] => {
  const start = normalizeDate(startTime);
  const end = normalizeDate(endTime);
  start.setMinutes(0, 0, 0);
  end.setMinutes(0, 0, 0);

  return iterateDates(start, end, (date) => date.setHours(date.getHours() + 1));
};

export const getDatesBetween = (startTime: string | Date, endTime: string | Date): string[] => {
  const start = normalizeDate(startTime);
  const end = normalizeDate(endTime);
  start.setHours(0, 0, 0, 0);
  end.setHours(0, 0, 0, 0);

  return iterateDates(start, end, (date) => date.setDate(date.getDate() + 1));
};

export const getMonthsBetween = (startTime: string | Date, endTime: string | Date): string[] => {
  const start = normalizeDate(startTime);
  const end = normalizeDate(endTime);
  start.setDate(1);
  start.setHours(0, 0, 0, 0);
  end.setDate(1);
  end.setHours(0, 0, 0, 0);

  return iterateDates(start, end, (date) => date.setMonth(date.getMonth() + 1));
};

export const getYearsBetween = (startTime: string | Date, endTime: string | Date): string[] => {
  const start = normalizeDate(startTime);
  const end = normalizeDate(endTime);
  start.setMonth(0, 1);
  start.setHours(0, 0, 0, 0);
  end.setMonth(0, 1);
  end.setHours(0, 0, 0, 0);

  return iterateDates(start, end, (date) => date.setFullYear(date.getFullYear() + 1));
};
