// import Flatpickr
import Flatpickr from "stimulus-flatpickr";
import ShortcutButtonsPlugin from "shortcut-buttons-flatpickr";
import labelPlugin from "flatpickr/dist/plugins/labelPlugin/labelPlugin";

export default class extends Flatpickr {
  /*
   * defaultDate (optional): "startOfDay" | "endOfDay"
   */
  static values = { enableTime: Boolean, mode: String, defaultDate: String };
  static targets = ["start", "end"];

  initialize() {
    const datetimepicker = this.enableTimeValue === true;
    const mode = this.modeValue === "range" ? "range" : "single";
    // configure flatpickr options (locale set dynamically in connect())
    this.config = {
      altInput: true,
      altFormat: datetimepicker
        ? Spree.translations.flatpickr_datetime_format
        : Spree.translations.flatpickr_date_format,
      dateFormat: datetimepicker ? "Y-m-d H:i" : "Y-m-d",
      enableTime: datetimepicker,
      time_24hr: datetimepicker,
      plugins: this.plugins(mode, datetimepicker),
      mode,
    };
  }

  async connect() {
    const locale = await this.importFlatpickrLocale(I18n.base_locale);
    this.config = {
      ...this.config,
      locale,
    };

    super.connect();
    window.addEventListener("flatpickr:change", this.onChangeEvent);
    window.addEventListener("flatpickr:clear", this.clear);
  }

  disconnect() {
    super.disconnect();
    window.removeEventListener("flatpickr:change", this.onChangeEvent);
    window.removeEventListener("flatpickr:clear", this.clear);
  }

  clear = (e) => {
    this.fp.setDate(null);
  };

  open() {
    this.fp.element.dispatchEvent(new Event("focus"));
    if (!this.fp.selectedDates.length) {
      this.setDefaultDateValue();
    }
  }

  onChangeEvent = (e) => {
    if (this.modeValue === "range" && this.hasStartTarget && this.hasEndTarget) {
      // date range mode
      if (e.detail) {
        this.startTarget.value = e.detail.startDate;
        this.endTarget.value = e.detail.endDate;
      }
      this.fp.setDate([this.startTarget.value, this.endTarget.value]);
    } else if (e.detail.date) {
      // single date mode
      this.fp.setDate(e.detail.date);
    }
  };

  change(selectedDates, dateStr, instance) {
    if (this.hasStartTarget && this.hasEndTarget && this.modeValue === "range") {
      this.startTarget.value = selectedDates[0]
        ? this.fp.formatDate(selectedDates[0], this.config.dateFormat)
        : "";
      this.endTarget.value = selectedDates[1]
        ? this.fp.formatDate(selectedDates[1], this.config.dateFormat)
        : "";
      // Also, send event to be sure that ng-model is well updated
      // Send event only if range il valid, ie. start and end are not empty
      if (this.startTarget.value && this.endTarget.value) {
        this.startTarget.dispatchEvent(new Event("change"));
        this.endTarget.dispatchEvent(new Event("change"));
      }
    }
  }

  close() {
    // Send a change event to the input element to trigger the ng-change
    this.hasEndTarget && this.endTarget.dispatchEvent(new Event("change"));
  }

  // private

  plugins = (mode, datetimepicker) => {
    const buttons = [{ label: Spree.translations.close }];
    if (mode === "single") {
      buttons.unshift({
        label: datetimepicker ? Spree.translations.now : Spree.translations.today,
      });
    }
    return [
      ShortcutButtonsPlugin({
        button: buttons,
        onClick: this.onClickButtons,
      }),
      labelPlugin({}),
    ];
  };

  onClickButtons = (index, fp) => {
    // Memorize index used for the 'Close' and 'Today|Now' buttons
    // it has index of 1 in case of single mode (ie. can set Today or Now date)
    // it has index of 0 in case of range mode (no Today or Now button)
    const closeButtonIndex = this.modeValue === "range" ? 0 : 1;
    const todayButtonIndex = this.modeValue === "range" ? null : 0;
    switch (index) {
      case todayButtonIndex:
        fp.setDate(new Date(), true);
        break;
      case closeButtonIndex:
        fp.close();
        break;
    }
  };

  setDefaultDateValue() {
    if (this.defaultDateValue === "startOfDay") {
      this.fp.setDate(moment().startOf("day").format());
    } else if (this.defaultDateValue === "endOfDay") {
      /*
       * We use "startOf('day')" of tomorrow in order to not lose
       * the records between [23:59:00 ~ 23:59:59] of today
       */
      this.fp.setDate(moment().add(1, "days").startOf("day").format());
    }
  }

  async importFlatpickrLocale(localeCode) {
    // null tells flatpickr to fall back to its built-in english locale
    if (!localeCode || localeCode === "en") return null;

    try {
      const localeModule = await import(`flatpickr/dist/l10n/${localeCode}.js`);
      return localeModule.default?.[localeCode] ?? null;
    } catch {
      return null;
    }
  }
}
