/**
 * import dependencies
 */
import 'flatpickr';
import ElementHelpers from '../helpers/element-helpers';

/**
 * Declare global vars
 */
const attributeBase = 'data-rs-datepicker';

export class DatePicker {
  constructor(element) {
    this.element = element;
    this.input = ElementHelpers.getElementByAttributeWithinElement(
      this.element,
      this.attributes.input
    );
    //get attribute values
    this.currentDate = this.input.dataset.rsDatepickerDefaultIsCurrentDate
      ? new Date()
      : null;
    this.defaultDate = this.input.dataset.rsDatepickerDefaultDate
      ? this.input.dataset.rsDatepickerDefaultDate
      : this.currentDate;
    this.minDate = this.input.dataset.rsDatepickerMinDate;
    this.maxDate = this.input.dataset.rsDatepickerMaxDate;
    this.dateFormat = this.input.dataset.rsDatepickerDateFormat;
    this.altFormat = this.input.dataset.rsDatepickerAltFormat;
    this.weekNumbers = ElementHelpers.isDesktop()
      ? this.input.dataset.rsDatepickerWeeknumbers
      : false;
    this.firstWeekDay = this.input.dataset.rsDatepickerFirstWeekDay;
    this.clear = this.input.dataset.rsDatepickerClear || 'clear';
    this.setVariables();
    // localisation
    this.getLocalisationVariables();
    // create instance
    this.createFlatDatePicker();
  }

  /**
   * Declare HTML variables
   * SVG arrow templates
   */
  setVariables() {
    this.arrowRight = `<svg id="arrow-right" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
			<path d="M2,10.9374 L15.604,10.9374 L11.289,15.2964 C10.9,15.6884 10.904,16.3224 11.297,16.7104 C11.688,17.0994 12.323,17.0954 12.711,16.7034 L18.711,10.6414 C18.897,10.4524 19.001,10.1974 19,9.9324 C18.999,9.6664 18.892,9.4134 18.703,9.2264 L12.703,3.2894 C12.509,3.0964 12.254,3.0004 12,3.0004 C11.742,3.0004 11.484,3.0994 11.289,3.2964 C10.9,3.6884 10.904,4.3224 11.297,4.7104 L15.568,8.9374 L2,8.9374 C1.447,8.9374 1,9.3854 1,9.9374 C1,10.4894 1.447,10.9374 2,10.9374"/>
			</svg>`;
    this.arrowLeft = `<svg id="arrow-left" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
			<path d="M18,9.0624 L4.396,9.0624 L8.711,4.7034 C9.1,4.3114 9.096,3.6774 8.703,3.2894 C8.312,2.9004 7.677,2.9044 7.289,3.2964 L1.289,9.3584 C1.103,9.5474 0.999,9.8024 1,10.0674 C1.001,10.3334 1.108,10.5864 1.297,10.7734 L7.297,16.7104 C7.491,16.9034 7.746,16.9994 8,16.9994 C8.258,16.9994 8.516,16.9004 8.711,16.7034 C9.1,16.3114 9.096,15.6774 8.703,15.2894 L4.432,11.0624 L18,11.0624 C18.553,11.0624 19,10.6144 19,10.0624 C19,9.5104 18.553,9.0624 18,9.0624"/>
			</svg>`;
  }

  /**
   * Declare attribute constants
   */
  get attributes() {
    return {
      input: `${attributeBase}-input`
    };
  }

  /**
   * check if first day of the week is sunday | default is monday
   */
  sundayCheck() {
    return this.firstWeekDay === 'sunday' ? 0 : 1;
  }

  /**
   * Get all the variables for localisation
   */
  getLocalisationVariables() {
    this.defaultShortMonths =
      'jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec';
    this.defaultLongMonths =
      'january, february, march, april, may, june, july, august, september, october, november, december';
    this.defaultShortWeeks = 'su, mo, tu, we, th, fr, sa';
    this.shortMonths =
      this.input.dataset.rsDatepickerShortMonths ||
      (window.flatDatepickeri18n && window.flatDatepickeri18n.shortMonths) ||
      this.defaultShortMonths;
    this.longMonths =
      this.input.dataset.rsDatepickerLongMonths ||
      (window.flatDatepickeri18n && window.flatDatepickeri18n.longMonths) ||
      this.defaultLongMonths;
    this.shortWeeks =
      this.input.dataset.rsDatepickerShortWeeks ||
      (window.flatDatepickeri18n && window.flatDatepickeri18n.shortWeeks) ||
      this.defaultShortWeeks;
    this.setLocalisation();
  }

  /**
   * Set language/localisation for instance
   */
  setLocalisation() {
    this.locale = {
      firstDayOfWeek: this.sundayCheck(),
      weekAbbreviation: '#',
      weekdays: {
        shorthand: this.shortWeeks.split(', ')
      },
      months: {
        shorthand: this.shortMonths.split(', '),
        longhand: this.longMonths.split(', ')
      }
    };
  }

  /**
   * FlatPicker options_config object
   */
  flatOptions() {
    return {
      allowInput: true,
      altFormat: this.altFormat,
      altInput: true,
      dateFormat: this.dateFormat,
      defaultDate: this.defaultDate,
      locale: this.locale,
      maxDate: this.maxDate,
      minDate: this.minDate,
      weekNumbers: this.weekNumbers,
      monthSelectorType: 'static',
      nextArrow: this.arrowRight,
      prevArrow: this.arrowLeft,
      shorthandCurrentMonth: true,
      onReady: (dates, dateStr, instance) => {
        this.flatOnReady(dateStr, instance);
      },
      onChange: (dates, dateStr) => {
        this.flatOnChange(dateStr);
      },
      onClose: (dates, dateStr, instance) => {
        this.flatOnClose(instance);
      },
      errorHandler: this.flatOnError.bind(this)
    };
  }

  /**
   * Create the flat-picker instance
   */
  createFlatDatePicker() {
    this.input.flatpickr(this.flatOptions());
  }

  /**
   * Initialise when ready
   */
  flatOnReady(dateStr, instance) {
    //set initial value
    this.input.setAttribute('value', dateStr);
    //add clear button only on desktop view (input get native on mobile)
    if (ElementHelpers.isDesktop()) {
      instance.innerContainer.insertAdjacentHTML(
        'afterend',
        `<div class="clear">${this.clear}</div>`
      );
      const clearButton = instance.calendarContainer.querySelector('.clear');
      clearButton.addEventListener('click', this.clearDate(instance));
    }
    // add event listener to calender Icon
    // altInput creates an extra input after this.input, therefore the extra check
    const calenderIcon = instance.altInput
      ? this.input.nextElementSibling.nextElementSibling
      : this.input.nextElementSibling;
    calenderIcon.addEventListener('click', this.openDatePicker(instance));
    // checks if the manually input.value is valid and update directly on keystroke (without enter)
    const selectedInput = instance.altInput
      ? this.input.nextElementSibling
      : this.input;
    selectedInput.addEventListener(
      'input',
      this.updateValidValue(selectedInput, instance),
      true
    );
  }

  /**
   * Open the datepicker instance
   */
  openDatePicker(instance) {
    return () => {
      instance.open();
    };
  }

  /**
   * clear the input value
   */
  clearDate(instance) {
    return () => {
      instance.clear();
    };
  }

  /**
   * validate the input value and update accordingly
   */
  updateValidValue(selectedInput, instance) {
    return () => {
      const value = selectedInput.value;
      //replace slashes for dashes
      const checkedValue =
        typeof value !== 'number' ? value.replace(/\\|\//g, '-') : value;
      if (
        isNaN(checkedValue.charAt(0)) === false &&
        checkedValue.length !== 0
      ) {
        const parsedDate = instance.parseDate(checkedValue, this.altFormat);
        const formattedDate = instance.formatDate(parsedDate, this.altFormat);
        if (checkedValue === formattedDate) {
          instance.setDate(checkedValue, true, this.altFormat);
          instance.selectedDates.length === 0 && this.flatOnError(checkedValue);
        }
      }
    };
  }

  //set the input.value on change
  flatOnChange(dateStr) {
    this.input.setAttribute('value', dateStr);
  }

  //make sure the value has been set when closed
  flatOnClose(instance) {
    instance.setDate(instance.altInput.value, true, instance.config.altFormat);
  }

  flatOnError(checkedValue) {
    console.warn('Error: invalid date', checkedValue);
  }

  /**
   * Get selector
   */
  static getSelector() {
    return '[' + attributeBase + ']';
  }
}
