import moment from '@baserow/modules/core/moment'
import {
  getDateMomentFormat,
  getTimeMomentFormat,
  getDateHumanReadableFormat,
  getTimeHumanReadableFormat,
  getFieldTimezone,
  getCellTimezoneAbbr,
} from '@baserow/modules/database/utils/date'

const DATE_PICKER_FORMAT = 'YYYY-MM-DD'

/**
 * Mixin that introduces methods for the date field. This can both be used for a row
 * and grid view field.
 */
export default {
  data() {
    return {
      date: '',
      pickerDate: '',
      time: '',
      momentDate: null,
    }
  },
  computed: {
    /**
     * We need to watch the value and the date/time formats. This can easily be done
     * with a computed property.
     */
    valueAndFormats() {
      return `${this.value}|${this.field.date_format}|${this.field.date_time_format}|${this.field.date_force_timezone}`
    },
    fieldDateFormat() {
      return getDateMomentFormat(this.field.date_format)
    },
  },
  watch: {
    /**
     * When the value or one of the date formats changes we need to update the date
     * and time data with the correct values. This is because the date and time data
     * are directly visible to the user and not the value like most other fields.
     */
    valueAndFormats() {
      if (!this.editing) {
        this.setDateAndTime(this.field, this.value)
      }
    },
  },
  created() {
    this.setDateAndTime(this.field, this.value)
  },
  methods: {
    updateDateValue() {
      this.pickerDate = this.momentDate.format(DATE_PICKER_FORMAT)
      this.date = this.momentDate.format(this.fieldDateFormat)
    },
    updateTimeValue() {
      const timeFormat = getTimeMomentFormat(this.field.date_time_format)
      this.time = this.momentDate.format(timeFormat)
    },
    /**
     * When the date part is updated we also need to update the copy data which
     * contains the whole date(time) in the correct format. The copy contains the
     * value that is actually going to be saved.
     */
    updateDate(field, value) {
      const dateFormats = [DATE_PICKER_FORMAT, this.fieldDateFormat]
      const timezone = getFieldTimezone(field)
      let newDate = moment.utc(value, dateFormats, true)
      if (timezone !== null) {
        newDate = newDate.clone().tz(timezone, true)
      }

      if (newDate.isValid()) {
        this.updateCopy(field, {
          year: newDate.year(),
          month: newDate.month(),
          date: newDate.date(),
        })
        this.updateDateValue()
      } else {
        this.date = value
      }
    },
    /**
     * When the time part is updated we also need to update the copy data which
     * contains the whole date(time) in the correct format. The copy contains the
     * value that is actually going to be saved.
     */
    updateTime(field, value) {
      const timeFormats = ['hh:mm a', 'HH:mm']
      const timezone = getFieldTimezone(field)
      let newTime = moment.utc(value, timeFormats, true)
      if (timezone !== null) {
        newTime = newTime.clone().tz(timezone, true)
      }

      if (newTime.isValid()) {
        this.updateCopy(field, {
          hour: newTime.hour(),
          minute: newTime.minute(),
          second: 0,
        })
        this.updateTimeValue()
      } else {
        this.time = value
      }
    },
    /**
     * When the user uses the datepicker to choose a date, we also need to update
     * date data and the copy so that the correct date is visible for the user.
     */
    chooseDate(field, value) {
      const timezone = getFieldTimezone(field)
      let pickerDate = moment.utc(value)
      if (timezone !== null) {
        pickerDate = pickerDate.clone().tz(timezone, true)
      }
      this.updateDate(field, pickerDate.format(DATE_PICKER_FORMAT))
    },
    /**
     * When the user uses the time context to choose a time, we also need to update
     * time data and the copy so that the correct time is visible for the user.
     */
    chooseTime(field, value) {
      this.updateTime(field, value)
      this.time = value
    },
    /**
     * A helper method that allows updating the copy data by only changing certain
     * properties of a datetime. For example only the month could be updated.
     */
    updateCopy(field, values) {
      const existing = this.momentDate.set(values)
      this.copy = field.date_include_time
        ? existing.format()
        : existing.format('YYYY-MM-DD')
    },
    /**
     * Updates the date and time data by converting the value to the correct formats.
     */
    setDateAndTime(field, value) {
      const timezone = getFieldTimezone(field)
      if (value === null) {
        this.date = this.time = ''
        this.momentDate = moment.utc()
        if (timezone) {
          this.momentDate = this.momentDate
            .clone()
            .utcOffset(moment.tz(timezone).utcOffset())
        }
        this.pickerDate = ''
        return
      }

      let existing = moment.utc(value, moment.ISO_8601, true)
      if (timezone) {
        existing = existing.clone().utcOffset(moment.tz(timezone).utcOffset())
      }

      this.momentDate = existing
      this.updateDateValue()
      this.updateTimeValue()
    },
    getCellTimezoneAbbr(field, value, force) {
      return getCellTimezoneAbbr(field, value, { force })
    },
    /**
     * Returns a human readable date placeholder of the format for the input.
     */
    getDatePlaceholder(field) {
      return this.$t(
        'humanDateFormat.' + getDateHumanReadableFormat(field.date_format)
      )
    },
    /**
     * Returns a human readable time placeholder of the format for the input.
     */
    getTimePlaceholder(field) {
      return getTimeHumanReadableFormat(field.date_time_format)
    },
    /**
     * When the user focuses on one of the inputs the related context menu must
     * also be opened.
     */
    focus(context, event) {
      context.toggle(event.currentTarget, 'bottom', 'left', 0)
    },
    /**
     * When the user blurs one of the inputs the related context menu must also be
     * hidden.
     */
    blur(context, event) {
      context.hide()
    },
  },
}