<template>
  <div class="itf-datepicker-inline">
    <div ref="calendar"></div>

    <div class="air-datepicker -inline-" v-if="!onlyCalendar">
      <nav class="air-datepicker-nav align-items-center">
        <div class="air-datepicker-title">{{$t('components.relativeDates')}}</div>
      </nav>

      <div class="d-grid gap-1 p-2">
        <button
          v-for="(item, n) in daysList"
          :key="n"
          :disabled="!isEnabled(item.date())"
          :class="{'active': isSelected(item.date())}"
          @click="selectDate(item.date())"
          class="btn btn-outline-primary btn-sm text-start">
          {{item.title}}
        </button>
      </div>
    </div>
  </div>
</template>
<script>
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import { DateTime } from 'luxon';
import ITFSettings from '../../ITFSettings';

export default @Component({
  name: 'itfDatePickerInline',
})
class itfDatePickerInline extends Vue {
  @Prop() value;
  @Prop({ type: String, default: 'yyyy-MM-dd' }) valueFormat;
  @Prop({ type: String, default: ITFSettings.defaultDisplayDateFormat }) displayFormat;
  @Prop({ type: Boolean, default: false }) onlyCalendar;
  @Prop({ type: Object, default: () => ({}) }) customDays;
  @Prop({ type:  [String, Date], default: '' }) minDate;
  @Prop({ type:  [String, Date], default: '' }) maxDate;
  @Prop({ type:  String, default: 'days' }) minView;
  @Prop({
    type: Array,
    default: function () {
      return [// { title: 'Today', date: () => [DateTime.local(), DateTime.local()] },
        { title: this.$t('components.thisWeek'), date: () => [DateTime.local().startOf('week'), DateTime.local().endOf('week')] },
        { title: this.$t('components.lastWeek'), date: () => [DateTime.local().minus({ week: 1 }).startOf('week'), DateTime.local().minus({ week: 1 }).endOf('week')] },
        { title: this.$t('components.thisMonth'), date: () => [DateTime.local().startOf('month'), DateTime.local().endOf('month')] },
        { title: this.$t('components.lastMonth'), date: () => [DateTime.local().minus({ months: 1 }).startOf('month'), DateTime.local().minus({ months: 1 }).endOf('month')] },
        { title: this.$t('components.thisQuarter'), date: () => [DateTime.local().startOf('quarter'), DateTime.local().endOf('quarter')] },
        { title: this.$t('components.lastQuarter'), date: () => [DateTime.local().minus({ quarter: 1 }).startOf('quarter'), DateTime.local().minus({ quarter: 1 }).endOf('quarter')] },
        { title: this.$t('components.thisYear'), date: () => [DateTime.local().startOf('year'), DateTime.local().endOf('year')] },
        { title: this.$t('components.lastYear'), date: () => [DateTime.local().minus({ year: 1 }).startOf('year'), DateTime.local().minus({ year: 1 }).endOf('year')] },
      ];
    }
  }) daysList;
  @Prop(Boolean) compare;
  @Prop({ type: Array }) range; // for compare mode

  calendar = null;

  mounted() {
    this.createCalendar();
  }

  compareMode(value) {
    return this.calendar.compareMode(value);
  }

  @Watch('minDate')
  @Watch('maxDate')
  @Watch('minView')
  async createCalendar() {
    if (this.calendar) {
      this.destroyCalendar();
    }
    const [
      { default: AirDatepicker },
      { default: localeEn },
      { default: localeUk },
      { default: localeDe }
    ] = await Promise.all([
      import('babel-loader!../../../libs/air-datepicker/air-datepicker'),
      import('babel-loader!../../../libs/air-datepicker/locale/en'),
      import('babel-loader!../../../libs/air-datepicker/locale/uk'),
      import('babel-loader!../../../libs/air-datepicker/locale/de')
    ]);

    const locales = {
      en: localeEn,
      uk: localeUk,
      de: localeDe,
    };
    const opts = {
      locale: locales[this.$i18n.locale] || locales.en,
      firstDay: 1,
      altFieldDateFormat: this.valueFormat,
      minDate: this.minDate,
      maxDate: this.maxDate,
      view: this.minView,
      minView: this.minView,
      range: true,
      compareRange: true,
      dynamicRange: true,
      toggleSelected: function({ date, datepicker }) {
        if (datepicker.selectedDates.length > 1) {
          datepicker.selectDate(date);
        }
        return false;
      },
      selectedDates: this.valueAsLuxon
        ? [this.valueAsLuxon[0].toJSDate(), this.valueAsLuxon[1].toJSDate()]
        : [],
      onSelect: () => {
        if (!this.calendar.rangeDateTo) {
          return;
        }
        this.updateValue(
          DateTime.fromJSDate(this.calendar.rangeDateFrom).toFormat(this.displayFormat),
          DateTime.fromJSDate(this.calendar.rangeDateTo).toFormat(this.displayFormat),
          false,
        );
      },
      onRenderCell: ({ date }) => {
        const strDate = DateTime.fromJSDate(date).toFormat('yyyy-MM-dd');
        if (!this.customDays[strDate]) {
          return { html: false, classes: false };
        }
        return {
          html: this.customDays[strDate].text || false,
          classes: this.customDays[strDate].class || false,
        };
      },
    };
    if (this.compare) {
      opts.compareRange = true;
    }

    this.calendar = new AirDatepicker(this.$refs.calendar, opts);
    if (this.valueAsLuxon) {
      this.calendar.setViewDate(this.valueAsLuxon[0].toJSDate());
    }
    if (this.compare) {
      this.calendar.compareMode(true);
    }
    this.onRangeChange();
  }

  beforeDestroy() {
    this.destroyCalendar();
  }

  destroyCalendar() {
    if (!this.calendar) {
      return;
    }
    this.calendar.destroy();
    this.calendar = null;
  }

  get valueAsLuxon() {
    if (!this.value || this.value.length < 2 || !this.value[0] || !this.value[1]) {
      return null;
    }
    if (this.valueFormat === 'ISO') {
      return [
        DateTime.fromISO(this.value[0]),
        DateTime.fromISO(this.value[1]),
      ];
    }
    return [
      DateTime.fromFormat(this.value[0], this.valueFormat),
      DateTime.fromFormat(this.value[1], this.valueFormat),
    ];
  }

  updateValue(date1, date2, emitEmpty = false) {
    const val = date1 && DateTime.fromFormat(date1, this.displayFormat);
    const val2 = date2 && DateTime.fromFormat(date2, this.displayFormat);
    if (!val || !val.isValid || !val2 || !val2.isValid) {
      if (emitEmpty) {
        this.$emit('input', null);
      }
      return;
    }
    if ((this.minDateLuxon && (val < this.minDateLuxon || val2 < this.minDateLuxon)) ||
      (this.maxDateLuxon && (val > this.maxDateLuxon || val2 > this.maxDateLuxon))
    ) {
      return;
    }

    if (this.valueFormat === 'ISO') {
      return this.$emit('input', [val.toISO(), val2.toISO()]);
    }
    this.$emit('input', [
      val.toFormat(this.valueFormat),
      val2.toFormat(this.valueFormat),
    ]);
  }

  get minDateLuxon() {
    return this.minDate && this.dateLuxon(this.minDate);
  }
  get maxDateLuxon() {
    return this.maxDate && this.dateLuxon(this.maxDate);
  }

  dateLuxon(date) {
    return typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date);
  }

  isEnabled(date) {
    const lxDate1 = date[0].set({ hours: 0, minutes: 0, seconds: 0 });
    const lxDate2 = date[1].set({ hours: 23, minutes: 59, seconds: 59 });

    if (this.minDateLuxon) {
      if (this.maxDateLuxon) {
        return lxDate1 > this.minDateLuxon && lxDate2 > this.minDateLuxon && lxDate1 < this.maxDateLuxon && lxDate2 < this.maxDateLuxon;
      } else {
        return lxDate1 > this.minDateLuxon && lxDate2 > this.minDateLuxon;
      }
    } else if (this.maxDateLuxon) {
      return lxDate1 < this.maxDateLuxon && lxDate2 < this.maxDateLuxon;
    }

    return true;
  }

  isSelected(date) {
    if (!this.valueAsLuxon || this.valueAsLuxon.length < 2) {
      return false;
    }
    return this.valueAsLuxon[0].hasSame(date[0], 'day') &&
      this.valueAsLuxon[1].hasSame(date[1], 'day');
  }

  selectDate(date) {
    const lxDate1 = date[0].set({ hours: 0, minutes: 0, seconds: 0 });
    const lxDate2 = date[1].set({ hours: 23, minutes: 59, seconds: 59 });
    this.updateValue(lxDate1.toFormat(this.displayFormat), lxDate2.toFormat(this.displayFormat));
    this.calendar.selectDate([lxDate1.toJSDate(), lxDate2.toJSDate()], { silent: true });
  }

  @Watch('range')
  onRangeChange() {
    if (!this.calendar) {
      return;
    }
    this.calendar.selectCompareRange(this.range ? [
      DateTime.fromFormat(this.range[0], this.valueFormat).toJSDate(),
      DateTime.fromFormat(this.range[1], this.valueFormat).toJSDate(),
    ] : []);
  }

  @Watch('value')
  onValueChanged() {
    if (!this.valueAsLuxon || !this.calendar) {
      this.calendar && this.calendar.clear({ silent: true });
      return;
    }
    const date1 = this.calendar.rangeDateFrom && DateTime.fromJSDate(this.calendar.rangeDateFrom).toFormat('yyyy-MM-dd')
    const date2 = this.calendar.rangeDateTo && DateTime.fromJSDate(this.calendar.rangeDateTo).toFormat('yyyy-MM-dd')
    if (date1 !== this.valueAsLuxon[0].toFormat('yyyy-MM-dd') || date2 !== this.valueAsLuxon[1].toFormat('yyyy-MM-dd')) {
      this.$nextTick(() => {
        this.calendar.selectDate(this.valueAsLuxon && [this.valueAsLuxon[0].toJSDate(), this.valueAsLuxon[1].toJSDate()], { silent: true });
      });
    }
  }
}
</script>
