class CurrentDate extends HTMLElement {
  constructor() {
    super();
    this.render();
  }

  // Static property to return time zone offsets in hours
  static get timeZoneOffsets() {
    return {
      UTC: 0,
      EST: -5,
      EDT: -4,
      CST: -6,
      CDT: -5,
      MST: -7,
      MDT: -6,
      PST: -8,
      PDT: -7,
      AKST: -9,
      AKDT: -8,
      HST: -10,
      HDT: -9,
    };
  }

  // Static property to return IANA time zone names
  static get timeZoneNames() {
    return {
      UTC: "UTC",
      EST: "America/New_York",
      EDT: "America/New_York",
      CST: "America/Chicago",
      CDT: "America/Chicago",
      MST: "America/Denver",
      MDT: "America/Denver",
      PST: "America/Los_Angeles",
      PDT: "America/Los_Angeles",
      AKST: "America/Anchorage",
      AKDT: "America/Anchorage",
      HST: "Pacific/Honolulu",
      HDT: "Pacific/Honolulu",
    };
  }

  /**
   * Get the offset in milliseconds for a given time zone.
   * @param {string} time_zone - The time zone identifier (e.g., "EST").
   * @returns {number} The offset in milliseconds.
   */
  getOffset(time_zone) {
    return (CurrentDate.timeZoneOffsets[time_zone] || 0) * 60 * 60000;
  }

  /**
   * Map the provided time zone to the corresponding IANA time zone name.
   * @param {string} time_zone - The time zone identifier (e.g., "EST").
   * @returns {string} The IANA time zone name.
   */
  timeZoneNameMapping(time_zone) {
    return CurrentDate.timeZoneNames[time_zone] || "UTC";
  }

  /**
   * Calculate the estimated delivery date.
   * @param {number} delivery_days - The number of days required for delivery.
   * @param {string} cut_off_time - The cut-off time in "HH:MM" format.
   * @param {boolean} disable_weekends - Whether to exclude weekends from delivery days.
   * @param {string} time_zone - The time zone identifier (e.g., "EST").
   * @returns {string} The formatted estimated delivery date.
   */
  getEstimatedDeliveryDate(
    delivery_days,
    cut_off_time,
    disable_weekends,
    time_zone
  ) {
    const offset = this.getOffset(time_zone);
    const currentTimeZoneDate = new Date(
      new Date().getTime() + new Date().getTimezoneOffset() * 60000 + offset
    );
    const [cutOffHours, cutOffMinutes] = cut_off_time.split(":").map(Number);
    const cutOffTimeInMinutes = cutOffHours * 60 + cutOffMinutes;
    const currentMinutesInTimeZone =
      currentTimeZoneDate.getHours() * 60 + currentTimeZoneDate.getMinutes();

    if (currentMinutesInTimeZone > cutOffTimeInMinutes) {
      delivery_days++;
    }

    let estimatedDeliveryDate = new Date(currentTimeZoneDate);
    while (delivery_days > 0) {
      estimatedDeliveryDate.setDate(estimatedDeliveryDate.getDate() + 1);
      const dayOfWeek = estimatedDeliveryDate.getDay();
      if (disable_weekends && (dayOfWeek === 0 || dayOfWeek === 6)) {
        continue;
      }
      delivery_days--;
    }
    return this.formatDateWithOrdinal(estimatedDeliveryDate, time_zone);
  }

  /**
   * Format the date with an ordinal suffix (e.g., "Thursday, June 6th").
   * @param {Date} date - The date to format.
   * @param {string} time_zone - The time zone identifier (e.g., "EST").
   * @returns {string} The formatted date with an ordinal suffix.
   */
  formatDateWithOrdinal(date, time_zone) {
    const options = {
      weekday: "long",
      month: "long",
      day: "numeric",
      timeZone: this.timeZoneNameMapping(time_zone),
    };
    const formatter = new Intl.DateTimeFormat("en-US", options);
    const parts = formatter.formatToParts(date);
    const day = parts.find((part) => part.type === "day").value;
    return `${parts.map((part) => part.value).join("")}${this.getOrdinalSuffix(
      day
    )}`;
  }

  /**
   * Get the ordinal suffix for a given day.
   * @param {number} day - The day of the month.
   * @returns {string} The ordinal suffix ("st", "nd", "rd", "th").
   */
  getOrdinalSuffix(day) {
    const j = day % 10,
      k = day % 100;
    if (j == 1 && k != 11) return "st";
    if (j == 2 && k != 12) return "nd";
    if (j == 3 && k != 13) return "rd";
    return "th";
  }

  /**
   * Convert a time in "HH:MM" format to 12-hour format with AM/PM.
   * @param {string} cut_off_time - The time in "HH:MM" format.
   * @returns {string} The time in 12-hour format with AM/PM.
   */
  convertTo12HourFormat(cut_off_time) {
    let [hours, minutes] = cut_off_time.split(":").map(Number);
    const ampm = hours >= 12 ? "PM" : "AM";
    hours = hours % 12 || 12;
    const formattedHours = hours.toString().padStart(2, '0');
    const formattedMinutes = minutes.toString().padStart(2, '0');
    return `${formattedHours}:${formattedMinutes}${ampm}`;
  }

  /**
   * Render the component.
   */
  render() {
    const {
      disable_weekends,
      delivery_days,
      truck_icon,
      message,
      cut_off_time,
      time_zone,
    } = JSON.parse(this.dataset.settings);
    let cleanDate = this.getEstimatedDeliveryDate(
      delivery_days,
      cut_off_time,
      disable_weekends,
      time_zone
    );
    let cleanMessage = message
      .replace("*date*", cleanDate)
      .replace("*time_zone*", time_zone)
      .replace("*hour*", this.convertTo12HourFormat(cut_off_time));
    this.innerHTML = `
          <div class="delivery-date-container" part="container">
            <div class="delivery-date-icon" part="icon">${truck_icon}</div>
            <div class="rte" part="content">${cleanMessage}</div>
          </div>
        `;
  }
}

customElements.define("current-date", CurrentDate);
