import { CalendarDate } from '../../../../../../helpers/calendar';
import { VegaInternalTranslation } from '../../../../../../helpers/translation/internal-translation-controller';
import { CalendarEventBaseRepeatPattern } from './base-repeat-pattern';
/** The `CalendarEventMonthlyRepeatPattern` class in TypeScript represents a monthly repeat pattern for calendar events. */
export class CalendarEventMonthlyRepeatPattern extends CalendarEventBaseRepeatPattern {
  /**
   * The constructor initializes the event with specified start and end dates, interval, count, and until date, and sets the byMonthDay property to the start date's day.
   *
   * @param {CalendarDate} startDate - The `startDate` parameter represents the starting date of the
   * event or interval. It is of type `CalendarDate`, which likely includes the year, month, and day
   * components of the date.
   * @param {number} interval - The `interval` parameter specifies the frequency of the recurring
   * events. For example, if the interval is set to 2, the event will occur every 2 units of time (days,
   * weeks, months, etc.).
   * @param {Nullable<number>} count - The `count` parameter specifies the number of occurrences of the event. If `count`
   * is provided, the event will repeat `count` times within the specified interval. If `count` is not
   * provided (or is `null`), the event will repeat indefinitely based on the other parameters like `
   * @param {Nullable<CalendarDate>} until - The `until` parameter is a nullable `CalendarDate` that specifies the end date for
   * the recurring event. If provided, the event will repeat until this date. If not provided, the event
   * will repeat indefinitely based on the other parameters like `count` or `endDate`.
   */
  constructor(startDate, interval, count, until) {
    super(startDate, interval, count, until);
    this.byMonthDay = this.repeatStartDate.date;
    this.initCache();
  }
  /**
   * Generate repeat display string.
   *
   * @returns {string} repeat display string
   */
  generateRepeatString() {
    const repeatString = this.interval > 1
      ? VegaInternalTranslation.t('Repeat every {0} months on day {1}', this.interval, this.byMonthDay)
      : VegaInternalTranslation.t('Repeat monthly on day {0}', this.byMonthDay);
    return repeatString + this.generateRepeatEndsString();
  }
  /**
   * This function generates and caches repeated dates before a specified end date.
   *
   * @param {CalendarDate} endDate - The `endDate` parameter in the
   * `generateRepeatDateCacheUntilEndDate` function is a CalendarDate object that represents the end
   * date until which the repeat dates need to be generated and cached. The function checks if the last
   * cached date is before the `endDate` and generates additional repeat dates until it reaches
   */
  generateRepeatDateCacheUntilEndDate(endDate) {
    const lastMatchDate = this.cache[this.cache.length - 1];
    if (lastMatchDate.toDate() < endDate.toDate() &&
      (!this.count || (typeof this.count === 'number' && this.cache.length < this.count))) {
      let nextMatchDate = this.getNextRepeatStartDate(lastMatchDate);
      while (nextMatchDate && nextMatchDate.toDate() < endDate.toDate()) {
        const newNextMatchDate = this.getNextRepeatStartDate(nextMatchDate);
        //The below if block is used to break the infinite loop when the `getNextRepeatStartDate` method return unexpected result. such as when the date is greater than a certain value, it will be returned back 1900.
        if (newNextMatchDate && newNextMatchDate.toDate() <= nextMatchDate.toDate()) {
          this.cache.pop();
          break;
        }
        nextMatchDate = newNextMatchDate;
        if (typeof this.count === 'number' && this.cache.length >= this.count) {
          break;
        }
      }
    }
  }
  /**
   * The function `initCache` initializes a cache array with the startDate value.
   */
  initCache() {
    this.cache = [this.repeatStartDate];
  }
  /**
   * The getNextRepeatStartDate function calculates the next start date based on a given date and interval, considering constraints such as end date and count limit.
   *
   * @param {CalendarDate} date - The `date` parameter represents the current date for which you want to
   * calculate the next repeat start date. It is of type `CalendarDate`.
   * @returns {Nullable<CalendarDate>} The function `getNextRepeatStartDate` returns a nullable `CalendarDate`. It either returns
   * the next calculated `CalendarDate` based on the input date and interval, or it returns `null` if
   * certain conditions are met (such as reaching the specified end date or count limit).
   */
  getNextRepeatStartDate(date) {
    const nextDate = this.getNextStartDate(date.year, date.month - 1 + this.interval);
    const nextCalendarDate = CalendarDate.from(nextDate);
    if ((this.until && nextDate > this.until.toDate()) ||
      (typeof this.count === 'number' && this.cache.length >= this.count)) {
      return null;
    }
    else {
      this.cache.push(nextCalendarDate);
      return nextCalendarDate;
    }
  }
  /**
   * get the new date with fixed date, if the date.getDate() not equal the fixed date, find the next month
   *
   * @param {number} year year
   * @param {number} month month
   * @returns {Date} the new date
   */
  getNextStartDate(year, month) {
    const nextDate = new Date(year, month, this.byMonthDay);
    if (nextDate.getDate() === this.byMonthDay) {
      return nextDate;
    }
    else {
      return this.getNextStartDate(year, month + 1);
    }
  }
}
