import calendarEventRepeatPatternFactory from '../../components/vega-calendar/slimmers/common/helpers/repeat-pattern/calendar-event-repeat-pattern-factory';
import { generateUUID } from '../../utils/misc';
import { VegaInternalTranslation } from '../translation/internal-translation-controller';
import { CalendarDate } from './calendar-date';
import { CalendarTime } from './calendar-time';
import { CalendarDateTime } from './calendar-date-time';
import { getSystemTimezone, WeekdayLongNames } from './utils';
const CalendarDateFormatReg = /^\d{1,2}\/\d{1,2}\/\d{4}/;
const CalendarTimeFormatReg = /^\d{1,2}:\d{1,2}/;
/** The `CalendarEvent` class in TypeScript represents a calendar event with methods to validate required fields, initialize dates and times, and determine if it is a group event. */
export class CalendarEvent {
  /**
   * The constructor initializes a VegaCalendarRawEvent object and performs validation checks on required fields.
   *
   * @param {VegaCalendarRawEvent} eventOptions - The `eventOptions` parameter in the constructor
   * represents the raw event data for a Vega calendar event. It contains information such as the event
   * title, start and end dates, location, description, and any other relevant details about the event.
   * @param {string} eventKey If a value is not provided for `eventKey`, a UUID (Universally
   * Unique Identifier) will be generated as a default value. This identifier is used to distinguish
   * different events and can be helpful for tracking
   * @param {string} timezone - Optional timezone identifier for timezone-aware date calculations
   */
  constructor(eventOptions, eventKey, timezone) {
    // the default value is 1 when the event end date is same as start date
    this._eventSpansDays = 1;
    this.repeatPattern = null;
    this._event = eventOptions;
    this._eventKey = eventKey || generateUUID();
    this._timezone = timezone || getSystemTimezone();
    this.checkEventRequireField(eventOptions);
    this.initDate(eventOptions);
    this.initEventSpansDays();
    this.initRepeatPattern();
  }
  /**
   * The function returns the raw event data for a VegaCalendar event.
   *
   * @returns {VegaCalendarRawEvent} The `get event()` method is returning a `VegaCalendarRawEvent` object, which is stored in the `_event` property.
   */
  get event() {
    return this._event;
  }
  /**
   * This function returns the key of an event in TypeScript.
   *
   * @returns {string} event key
   */
  get eventKey() {
    return this._eventKey;
  }
  /**
   * This function returns the title of an event in TypeScript.
   *
   * @returns {string} The title of the event.
   */
  get title() {
    return this._event.title;
  }
  /**
   * This function returns the disabled of an event in TypeScript.
   *
   * @returns {boolean} The disabled of the event.
   */
  get disabled() {
    return !!this._event.disabled;
  }
  /**
   * This function returns the variant of an event in TypeScript.
   *
   * @returns {Nullable<VegaCalendarEventVariantType>} The variant of the event.
   */
  get variant() {
    return this._event.variant;
  }
  /**
   * This function returns the color of an event in TypeScript.
   *
   * @returns {Nullable<string>} The color of the event.
   */
  get color() {
    return this._event.color;
  }
  /**
   * The function isAllDayEvent returns a boolean value indicating whether the event is an all-day event.
   *
   * @returns {boolean} The method `isAllDayEvent()` is returning a boolean value indicating whether the event is an all-day event or not. It returns `true` if the event is an all-day event, and `false` if it is not.
   */
  get isAllDayEvent() {
    return !!this._event.isAllDay;
  }
  /**
   * The function isRepeatEvent returns a boolean value based on whether the event's repeatOptions property is not equal to 'none'.
   *
   * @returns {boolean} The method `isRepeatEvent` returns a boolean value indicating whether the `_event` has a repeat value other than 'none'.
   */
  get isRepeatEvent() {
    return this.repeatPattern !== null;
  }
  /**
   * The function returns the start date property of an object.
   *
   * @returns {Date} The `startDate` property of type `Date` is being returned.
   */
  get startDate() {
    return this._startDateTime.toDate();
  }
  /**
   * The function returns the end date property of an object.
   *
   * @returns {Date} The `endDate` property of type `Date` is being returned.
   */
  get endDate() {
    return this._endDateTime.toDate();
  }
  /**
   * This function returns the start time as a CalendarTime.
   *
   * @returns {CalendarTime} The event start time.
   */
  get startTime() {
    return this._startDateTime.calendarTime;
  }
  /**
   * This function returns the end time as a CalendarTime.
   *
   * @returns {CalendarTime} The event end time.
   */
  get endTime() {
    return this._endDateTime.calendarTime;
  }
  /**
   * This function returns the number of days that an event spans.
   *
   * @returns {number} The method `get eventSpansDays()` is returning the value of the property
   * `_eventSpansDays`, which is of type `number`.
   */
  get eventSpansDays() {
    return this._eventSpansDays;
  }
  /**
   * The function `copyTo` copies a calendar event with updated start and end dates.
   *
   * @param {string} eventKey - The `eventKey` parameter in the `copyTo` function is a string that represents the key or identifier of the new calendar event that will be created.
   * @param {CalendarDate} startDate - The `startDate` parameter in the `copyTo` function represents
   * the starting date of a calendar event. It is of type `CalendarDate`, which may contain information such as the day, month, and year of the event.
   * @returns {CalendarEvent} A new `CalendarEvent` object is being returned with the properties from the original
   * `_event` object (excluding `repeatOptions`) and the `startDate` and `endDate` properties set to
   * the `toDate()` values of the `startDate` and `endDate` parameters.
   */
  copyTo(eventKey, startDate) {
    const endDate = this._eventSpansDays > 1
      ? new Date(startDate.year, startDate.month - 1, startDate.date + (this._eventSpansDays - 1))
      : startDate.toDate();
    const result = new CalendarEvent(Object.assign(Object.assign({}, this._event), { startDate: startDate.toDate(), endDate: endDate, startTime: this.startTime.toString(), endTime: this.endTime.toString() }), eventKey);
    return result;
  }
  /**
   * Check the event is a group event or not
   *
   * @returns {boolean} true or false
   */
  isGroupEvent() {
    return this._eventSpansDays > 1;
  }
  /**
   * This TypeScript function checks if a given date falls within the range of a calendar event based on its start and end dates, considering different repeat types like daily, weekly, monthly, and yearly.
   *
   * @param {CalendarDate} date - The `date` parameter represents the current date for which you want to
   * check if it falls within the range of the `calendarEvent`. It is of type `CalendarDate`.
   * @returns {boolean} a boolean value, either `true` or `false`, based on whether the `calendarEvent` falls on the `date` provided.
   */
  isCurrentDayEvent(date) {
    const { startDate, endDate } = this;
    const startCalendarDate = CalendarDate.from(startDate);
    const endCalendarDate = CalendarDate.from(new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate() + 1));
    const currentDate = date.toDate();
    return startCalendarDate.toDate() <= currentDate && endCalendarDate.toDate() > currentDate;
  }
  /**
   * Generate repeat display string.
   *
   * @returns {string} repeat display string
   */
  generateRepeatString() {
    var _a;
    return ((_a = this.repeatPattern) === null || _a === void 0 ? void 0 : _a.generateRepeatString()) || '';
  }
  /**
   * Generate date display string.
   *
   * @param {CalendarDate} viewDate - The `date` to display
   * @param {TimeFormat} timeFormat - The `timeFormat` to use
   * @returns {string} repeat display string
   */
  generateDateTimeString(viewDate, timeFormat) {
    const weekDay = VegaInternalTranslation.t(Object.values(WeekdayLongNames)[viewDate.toDate().getDay()]);
    const startTime = this.startTime.toString(timeFormat);
    const endTime = this.endTime.toString(timeFormat);
    return `${weekDay}, ${viewDate.date} ${viewDate.getMonthName(true)} ${viewDate.year} ${startTime} - ${endTime}`;
  }
  /**
   * Get the number of minutes the event lasted
   *
   * @returns {number} the minutes
   */
  getEventMinutes() {
    return CalendarTime.calculateDuration(this.startTime, this.endTime, 'minutes');
  }
  /**
   * This function returns an array of repeat series based on a given calendar period.
   *
   * @param {CalendarPeriod} period - The `period` parameter in the `getRepeatSeries` method is of type
   * `CalendarPeriod`. It represents a specific time period within a calendar, typically defined by a
   * start date and an end date. The method uses this period to generate a series of repeated events
   * based on a daily repeat pattern specified
   * @returns {CalendarDate[]} repeat start date array
   */
  getRepeatSeries(period) {
    if (this.repeatPattern) {
      return this.repeatPattern.getRepeatSeries(period, this._eventSpansDays);
    }
    return [];
  }
  /**
   * Refresh event dates and related properties when timezone changes
   *
   * @param {string} timezone - The new timezone to apply to the event
   */
  updateTimezone(timezone) {
    this._timezone = timezone;
    this._startDateTime.setTimezone(timezone);
    this._endDateTime.setTimezone(timezone);
  }
  /**
   * The function `checkEventRequireField` validates the required fields of a calendar event item in TypeScript.
   *
   * @param {VegaCalendarRawEvent} eventOptions - The `eventOptions` parameter is an object of type
   * `VegaCalendarRawEvent` that contains information about a calendar event
   */
  checkEventRequireField(eventOptions) {
    const { title, startDate, endDate, startTime, endTime } = eventOptions;
    if ((startDate && typeof startDate === 'string' && !CalendarDateFormatReg.test(startDate)) ||
      (endDate && typeof endDate === 'string' && !CalendarDateFormatReg.test(endDate))) {
      throw new Error('Invalid calendar event item, the date format should be MM/DD/YYYY!');
    }
    if ((startTime && typeof startTime === 'string' && !CalendarTimeFormatReg.test(startTime)) ||
      (endTime && typeof endTime === 'string' && !CalendarTimeFormatReg.test(endTime))) {
      throw new Error('Invalid calendar event item, the time format should be HH:mm!');
    }
    if (!title || !startDate || (!endDate && !endTime)) {
      throw new Error('Invalid calendar event item, the required field should not empty!');
    }
  }
  /**
   * The `initDate` function in TypeScript initializes start and end dates and times for a calendar event based on the provided options.
   *
   * @param {VegaCalendarRawEvent} eventOptions - The `initDate` method you provided is used to
   * initialize the start and end dates for a calendar event based on the options provided in the
   * `eventOptions` object. The `eventOptions` object contains properties such as `startDate`,
   * `startTime`, `endDate`, and `endTime` which are used
   */
  initDate(eventOptions) {
    const { startDate, startTime, endDate, endTime } = eventOptions;
    this.processStartDate(startDate, startTime);
    this.processEndDate(endDate, endTime, startDate);
  }
  /**
   * Parse the supplied start date/time, normalize into calendar wrappers.
   *
   * @param {string | Date} startDate - Raw start date value from the event payload.
   * @param {string} startTime - Optional textual time component to parse.
   */
  processStartDate(startDate, startTime) {
    this._startDateTime = CalendarDateTime.fromInitDataSource(startDate, {
      timezone: this._timezone,
      time: startTime,
    });
  }
  /**
   * Parse the supplied end date/time, normalize into calendar wrappers.
   *
   * @param {string | Date | undefined} endDate - The raw end date value from the event payload.
   * @param {string | undefined} endTime - The optional textual end time component to parse.
   * @param {string | Date} originStartDate - The start date value to use as fallback if endDate is missing.
   */
  processEndDate(endDate, endTime, originStartDate) {
    this._endDateTime = CalendarDateTime.fromInitDataSource(endDate || originStartDate, {
      timezone: this._timezone,
      time: endTime,
      endTimeMissing: !endTime && !endDate,
    });
  }
  /**
   * The function initializes a repeat pattern controller for a calendar event with specified start and end dates and repeat options.
   */
  initRepeatPattern() {
    if (this._event.repeatOptions && this._event.repeatOptions.frequency) {
      this.repeatPattern = calendarEventRepeatPatternFactory.getPattern(this.startDate, this._event.repeatOptions, this._timezone);
    }
  }
  /**
   * Initializes the event cross days by the event start date and end date.
   */
  initEventSpansDays() {
    const start = CalendarDate.from(this.startDate);
    const end = CalendarDate.from(this.endDate);
    this._eventSpansDays =
      (end.toDate().valueOf() - start.toDate().valueOf()) / (24 * 60 * 60 * 1000) + 1;
  }
}
