// @ts-nocheck
import Component from 'vue-class-component';
import Vue from 'vue';
import { QDate, QIcon, QInput, QPopupProxy, QTime } from 'quasar';
import { Prop, Watch } from 'vue-property-decorator';
import {
  FormGeneratorDateType,
  StringUtils,
} from '@devsun/form-generator/dist';
import {
  convertDateFormat,
  convertDateFromTimestamp,
  parseDateFromString,
  parseDate,
  isCorrectDate,
} from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import sub from 'date-fns/sub';
import add from 'date-fns/add';
import isBefore from 'date-fns/isBefore';
import isAfter from 'date-fns/isAfter';
import isToday from 'date-fns/isToday';

/**
 * prop - lowerDateBound, upperDateBound
 * имеет формат - [
 *  ['subtract' | 'add', number, 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' | 'years'],
 *  ['set', {'day', 'month', year}] - устанавливает дату, которая будет выбрана. Все, что не указано в объекте,
 *  будет расчитано через subtract | add или указана текущая дата.
 *  | 'today'
 * ]
 *
 * устанавливает границы выбора дат от сегодняшнего дня
 */

/**
 * Custom date picker
 * Quasar date picker with input field and modal/popover
 */
@Component({
  components: {
    QInput,
    QIcon,
    QPopupProxy,
    QDate,
    QTime,
  },
})
export default class ArmDatepicker extends Vue {
  //#region Props
  @Prop({ default: FormGeneratorDateType.none }) type: FormGeneratorDateType;
  @Prop() format: string;
  @Prop({ type: Boolean, default: false }) private maxToday?: boolean;
  @Prop({ type: Boolean, default: false }) private minToday?: boolean;
  @Prop({ type: String | null, default: null }) private maxDateLimit?:
    | string
    | null;
  @Prop({ type: String | null, default: null }) private minDateLimit?:
    | string
    | null;
  @Prop({ type: Boolean }) readonlyAsText: boolean;

  /**
   * Value for data from parent component
   */
  @Prop() private value: string;

  @Prop({ type: Boolean, default: false }) private readonly: boolean;
  @Prop({ type: Boolean, default: false }) private required: boolean;
  @Prop({ type: Boolean, default: false }) private disable: boolean;
  @Prop({ type: Boolean, default: false }) private steppedBack: boolean;
  @Prop({ type: Boolean, default: false }) private noLabels: boolean;

  @Prop({ type: String, default: '' }) private label: string;
  @Prop({ type: String, default: '1970/01' }) private minYearMonth: string;
  @Prop({ type: String }) private maxYearMonth: string;

  @Prop() private lowerDateBound;
  @Prop() private upperDateBound;

  @Prop() private color: boolean;

  @Prop() readonly validationError: boolean;
  @Prop() readonly validationMessage: string;
  @Prop() readonly fgErrorMessage: string;
  //#endregion

  /**
   * Date from input field passed manually
   */
  public displayDate: string = null;

  // /**
  //  * Action for input event on date picker
  //  */
  // private saveSelectedDate(selectedDate) {
  //   this.$emit('input', selectedDate)
  //   // this.$nextTick(() => this.$emit('input', selectedDate));
  // }

  public focus: boolean = false;

  $refs!: {
    input: HTMLInputElement;
  };

  /**
   * Converts date to ISO format to display on datepicker modal window
   */
  @Watch('displayDate')
  onManualInput(displayDate, oldVal) {
    // if (!oldVal) {
    //   return;
    // }
    if (!oldVal && !displayDate) {
      // not initialized yet
      return;
    }

    // no check for empty value
    if (!displayDate && !this.required) {
      this.$emit('input', '');
      return;
    }

    if (oldVal && StringUtils.isEmpty(displayDate) && this.required) {
      this.$emit('input', '');
      return;
    }
    const formatted = parseDateFromString(displayDate, this.pattern);
    const isError = !isCorrectDate(formatted);
    const isoString = convertDateFormat(formatted, '', DateFormat.backend);

    if (!isError) {
      this.$emit(
        'input',
        this.format ? convertDateFormat(formatted, '', this.format) : isoString
      );
    }
  }

  @Watch('hasError')
  onManualInputHasError(value) {
    if (value) {
      this.$emit('input', '');
    }
  }

  @Watch('value', { immediate: true })
  private onValueChange() {
    this.syncDisplayDate();
    this.$nextTick();
  }

  private mounted() {
    // if (this.value && !StringUtils.isEmpty(this.value.__id as string)) {
    //   // TODO : should be this.value not this.value.__id // @see NodeGenerator
    //   this.displayDate = convertDateFormat(this.value.__id, '', DateFormat.date)
    // }
    this.syncDisplayDate();
  }

  syncDisplayDate() {
    this.displayDate = this.getDisplayDate();
  }

  get mask() {
    return this.pattern;
  }

  get inputMask() {
    return this.mask.replace(/[a-z]/gi, '#');
  }

  get pattern() {
    switch (this.type) {
      case FormGeneratorDateType.datetime:
        return DateFormat.datetime;
      case FormGeneratorDateType.date:
        return DateFormat.date;
      case FormGeneratorDateType.time:
        return DateFormat.time;
    }
  }

  get isDateView(): boolean {
    return [
      FormGeneratorDateType.date,
      FormGeneratorDateType.datetime,
    ].includes(this.type);
  }

  get isTimeView(): boolean {
    return [
      FormGeneratorDateType.time,
      FormGeneratorDateType.datetime,
    ].includes(this.type);
  }

  private getBoundDate(config): Date {
    let date = new Date();
    const periodConfig = config.filter(
      (item) => item[0] === 'subtract' || 'add'
    );
    const setConfig = config.find((item) => item[0] === 'set');
    let missingDate = {};

    if (periodConfig.length) {
      for (const [direction, amount, unit] of periodConfig) {
        switch (direction) {
          case 'subtract':
            date = sub(date, { [unit]: amount });
            break;
          case 'add':
            date = add(date, { [unit]: amount });
            break;
        }
      }
    }

    if (setConfig) {
      const time = setConfig[1];
      const timeNames = ['day', 'month', 'year'];
      timeNames.forEach((name) => {
        missingDate[name] = time[name] || this.getPeriod(date, name);
      });
      date = Object.values(missingDate).join('.');
      return new Date(date);
    }

    return date;
  }

  private getPeriod(date, method) {
    switch (method) {
      case 'day':
        return date.getDate();
      case 'month':
        return date.getMonth();
      case 'year':
        return date.getFullYear();
    }
  }

  get lowerBoundRaw(): Date | null {
    return this.lowerDateBound ? this.getBoundDate(this.lowerDateBound) : null;
  }

  get upperBoundRaw(): Date | null {
    return this.upperDateBound ? this.getBoundDate(this.upperDateBound) : null;
  }

  get today(): Date {
    return new Date();
  }

  private dateRangeOptions(date: string): boolean {
    const formatedDate = convertDateFormat(
      date,
      DateFormat.yearMonthDay,
      DateFormat.invertedDate
    );
    const today = convertDateFromTimestamp(
      Math.floor(Date.now() / 1000),
      DateFormat.invertedDate
    );
    const dateObj = new Date(formatedDate);

    if (this.lowerDateBound && this.upperDateBound) {
      return (
        isAfter(dateObj, this.lowerBoundRaw) &&
        isBefore(dateObj, this.upperBoundRaw)
      );
    }

    if (this.maxToday) {
      const afterLowerBoundary = this.minDateLimit
        ? isAfter(dateObj, new Date(this.minDateLimit))
        : true;

      return (
        (isBefore(dateObj, this.today) || isToday(dateObj)) &&
        afterLowerBoundary
      );
    }

    if (this.minToday) {
      return this.maxDateLimit
        ? formatedDate >= today && formatedDate <= this.maxDateLimit
        : formatedDate >= today;
    }

    if (this.maxDateLimit || this.minDateLimit) {
      if (this.maxDateLimit && this.minDateLimit) {
        return (
          formatedDate <= this.maxDateLimit && formatedDate >= this.minDateLimit
        );
      } else {
        return this.maxDateLimit
          ? formatedDate <= this.maxDateLimit
          : formatedDate >= this.minDateLimit;
      }
    }

    return true;
  }

  getDisplayDate(): string {
    if (this.isDateView) {
      if (StringUtils.isEmpty(this.value)) return '';

      let date = this.format
        ? parseDate(this.value, this.format)
        : parseDateFromString(this.value, this.pattern);

      if (!this.format && !isCorrectDate(date)) {
        date = parseDateFromString(value, DateFormat.backend);
      }

      return isCorrectDate(date)
        ? convertDateFormat(date, '', this.pattern)
        : value;
    }

    return this.value;
  }

  openPopup(): void {
    if (!this.focus && !this.readonly) {
      this.$refs.qDateProxy.show();
      this.focus = true;
      this.$emit('focus');
    }
  }

  changeFocus(): boolean {
    return (this.focus = false);
  }

  get hasError(): boolean {
    return (
      this.validationError ||
      (this.minToday &&
        new Date(this.value) <
          convertDateFormat(this.today, '', this.pattern)) ||
      (this.maxToday &&
        new Date(this.value) >
          convertDateFormat(this.today, '', this.pattern)) ||
      (this.upperBoundRaw &&
        new Date(this.value) > new Date(this.upperBoundRaw)) ||
      (this.lowerBoundRaw &&
        new Date(this.value) < new Date(this.lowerBoundRaw))
    );
  }

  get errorMessage(): string {
    return this.fgErrorMessage ? this.fgErrorMessage : this.validationMessage;
  }

  get minYearMonthC(): string {
    if (this.lowerBoundRaw) {
      return convertDateFormat(
        this.lowerBoundRaw,
        '',
        DateFormat.slashedYearMonth
      );
    }

    return this.minYearMonth;
  }

  get maxYearMonthC(): string {
    if (this.upperBoundRaw) {
      return convertDateFormat(
        this.upperBoundRaw,
        '',
        DateFormat.slashedYearMonth
      );
    }

    return this.maxYearMonth;
  }

  get defaultYearMonthC(): string {
    return (
      this.maxYearMonthC ||
      convertDateFormat(new Date(), '', DateFormat.slashedYearMonth)
    );
  }
}
