<template>
  <div class="mm-datepicker-panel">
    <div class="mm-datepicker-panel-range">
      <div class="mm-start-panel">
        <div
          v-show="!selectTime"
          class="mm-datepicker-panel-header">
          <ui-icon
            name="chevronleft"
            size="0.875rem"
            @click="handleClickPrev('start')"></ui-icon>
          <h2>
            <a
              class="mm-datepicker-panel-header-label"
              @click="switchpanel('start', 'year')">
              {{ startYearLabel }}
            </a>
            <a
              v-show="startPanel === 'date'"
              class="mm-datepicker-panel-header-label"
              @click="switchpanel('start', 'month')">
              {{ t(`ui.datepicker.months[${startMonth}]`) }}
            </a>
          </h2>
          <ui-icon
            v-if="!hideStartNextMonth"
            name="chevronright"
            size="0.875rem"
            @click="handleClickNext('start')"></ui-icon>
          <span
            v-else
            class="icon-placeholder"></span>
        </div>
        <div
          v-show="selectTime"
          class="mm-datepicker-panel-header mm-datepicker-panel-header__static">
          {{ startStr }}
        </div>
        <component
          :is="startPanel"
          v-show="!selectTime"
          :start-date="start"
          :end-date="end"
          :year="startYear"
          :month="startMonth"
          :picked-date="start"
          :on-pick-year="handleYearPick.bind(null, 'start')"
          :on-pick-month="handleMonthPick.bind(null, 'start')"
          :on-pick="handlePick"
          :on-hover="handleHover"
          :selecting="selecting"
          :range="range"
          :disabled-date="startDisabledDate"></component>

        <date-time
          v-show="selectTime"
          v-model="startTime"
          :visible="selectTime"
          v-bind="disabledTime()"
          :format="format"></date-time>
      </div>
      <div class="mm-end-panel">
        <div
          v-show="!selectTime"
          class="mm-datepicker-panel-header">
          <ui-icon
            v-if="!hideEndPrevMonth"
            name="chevronleft"
            size="0.875rem"
            @click="handleClickPrev('end')"></ui-icon>
          <span
            v-else
            class="icon-placeholder"></span>
          <h2>
            <a
              class="mm-datepicker-panel-header-label"
              @click="switchpanel('end', 'year')">
              {{ endYearLabel }}
            </a>
            <a
              v-show="endPanel === 'date'"
              class="mm-datepicker-panel-header-label"
              @click="switchpanel('end', 'month')">
              {{ t(`ui.datepicker.months[${endMonth}]`) }}
            </a>
          </h2>
          <ui-icon
            name="chevronright"
            size="0.875rem"
            @click="handleClickNext('end')"></ui-icon>
        </div>
        <div
          v-show="selectTime"
          class="mm-datepicker-panel-header mm-datepicker-panel-header__static">
          {{ endStr }}
        </div>

        <component
          :is="endPanel"
          v-show="!selectTime"
          :start-date="start"
          :end-date="end"
          :year="endYear"
          :month="endMonth"
          :picked-date="end"
          :on-pick-year="handleYearPick.bind(null, 'end')"
          :on-pick-month="handleMonthPick.bind(null, 'end')"
          :on-pick="handlePick"
          :on-hover="handleHover"
          :selecting="selecting"
          :range="range"
          :disabled-date="endDisabledDate"></component>

        <date-time
          v-show="selectTime"
          v-model="endTime"
          :visible="selectTime"
          v-bind="disabledTime()"
          :format="format"></date-time>
      </div>
    </div>
    <div
      v-if="showBottom"
      class="mm-datepicker-panel-footer">
      <a
        :disabled="selectedRange.length !== 2"
        class="mm-datepicker-panel-switch"
        @click="handleSwitch">
        {{ startPanel === 'date-time' ? t('ui.datepicker.selectDate') : t('ui.datepicker.selectTime') }}
      </a>
      <ui-button
        :disabled="selectedRange.length !== 2"
        type="primary"
        size="small"
        @click="closePanel">
        {{ t("ui.datepicker.confirm") }}
      </ui-button>
    </div>
  </div>
</template>

<script>
import { formatDate, getDaysInMonth } from "../../../utils/date"
import Icon from "../../icon"
import Button from "../../button"
import DateTable from "../basic/date-table"
import MonthTable from "../basic/month-table"
import YearTable from "../basic/year-table"
import TimePanel from "../../time-picker/time-picker-panel"
import locale from "../../../mixins/locale"
import isEqual from "lodash/isEqual"

export default {
  name: "mm-date-panel",
  components: {
    "ui-icon": Icon,
    "ui-button": Button,
    date: DateTable,
    month: MonthTable,
    year: YearTable,
    "date-time": TimePanel,
  },
  mixins: [locale],
  props: {
    value: [Array, String],
    type: {
      type: String,
      default: "date",
      validator: value => {
        return ["year", "month", "date", "date-time"].includes(value)
      },
    },
    visible: Boolean,
    disabledDate: Function,
    disabledTime: {
      type: Function,
      default: () => {},
    },
    format: String,
  },
  data() {
    const start = this.value[0] ? new Date(this.value[0]) : ""
    const end = this.value[1] ? new Date(this.value[1]) : ""
    const now = new Date()
    const startYear = (start || now).getFullYear()
    const startMonth = (start || now).getMonth()
    const endYear = (end || now).getFullYear()
    const endMonth = (end || now).getMonth()
    const isInSameMonth = startYear === endYear && startMonth === endMonth
    const initialPanel = this.type === "date-time" ? "date" : this.type

    return {
      startPanel: initialPanel,
      endPanel: initialPanel,
      startDate: new Date(startYear, startMonth, 1), // for display panel
      endDate: new Date(endYear, isInSameMonth ? endMonth + 1 : endMonth, 1), // for display panel
      startTime: start || now, // for time picker
      endTime: end || now, // for time picker
      start: "", // selected start date
      end: "", // selected end date
      hover: "", // hovered date
    }
  },
  computed: {
    selecting() {
      return Boolean(this.start) + Boolean(this.end) === 1
    },
    startYear() {
      return this.startDate.getFullYear()
    },
    startMonth() {
      return this.startDate.getMonth()
    },
    startYearLabel() {
      let label = `${this.startYear} ${this.t("ui.datepicker.year")}`
      if (this.startPanel === "year") {
        const startYear = Math.floor(this.startYear / 10) * 10
        label = `${startYear} ${this.t("ui.datepicker.year")} - ${startYear + 9} ${this.t(
          "ui.datepicker.year"
        )}`
      }

      return label
    },
    endYear() {
      return this.endDate.getFullYear()
    },
    endMonth() {
      return this.endDate.getMonth()
    },
    endYearLabel() {
      let label = `${this.endYear} ${this.t("ui.datepicker.year")}`
      if (this.endPanel === "year") {
        const endYear = Math.floor(this.endYear / 10) * 10
        label = `${endYear} ${this.t("ui.datepicker.year")} - ${endYear + 9} ${this.t(
          "ui.datepicker.year"
        )}`
      }

      return label
    },
    range() {
      let arr = []
      if (this.selecting && this.hover) {
        arr = this.hover < this.start ? [this.hover, this.start] : [this.start, this.hover]
      }

      if (this.start && this.end) {
        arr = [this.start, this.end]
      }

      return arr
    },
    selectedRange() {
      return this.start && this.end ? [this.start, this.end] : []
    },
    startCloseToEnd() {
      return this.startYear === this.endYear && this.startMonth + 1 === this.endMonth
    },
    hideStartNextMonth() {
      return this.startPanel === "date" && this.startCloseToEnd
    },
    hideEndPrevMonth() {
      return this.endPanel === "date" && this.startCloseToEnd
    },
    selectTime() {
      return this.startPanel === "date-time"
    },
    startStr() {
      return this.selectedRange[0]
        ? formatDate(this.selectedRange[0], this.t("ui.datepicker.format.date"))
        : ""
    },
    endStr() {
      return this.selectedRange[1]
        ? formatDate(this.selectedRange[1], this.t("ui.datepicker.format.date"))
        : ""
    },
    showBottom() {
      const startPanel = this.startPanel === "date" || this.startPanel === "date-time"
      const endPanel = this.endPanel === "date" || this.endPanel === "date-time"

      return this.type === "date-time" && startPanel && endPanel
    },
  },
  watch: {
    value: {
      immediate: true,
      handler(value) {
        if (value && value.length === 2) {
          this.start = value[0]
          this.end = value[1]
        }
      },
    },
    start(value, oldValue) {
      this.startTime = value
      if (value && (!oldValue || value.getTime() !== oldValue.getTime())) {
        this.$emit("picked-start", value)
      }
    },
    end(value, oldValue) {
      this.endTime = value
      if (value && (!oldValue || value.getTime() !== oldValue.getTime())) {
        this.$emit("picked-end", value)
      }
    },
    startTime(value) {
      const start = this.selectedRange[0]
      if (start && value && start.getTime() !== value.getTime()) {
        this.start = new Date(
          start.getFullYear(),
          start.getMonth(),
          start.getDate(),
          ...this.getTime(value)
        )
      }
    },
    endTime(value) {
      const end = this.selectedRange[1]
      if (end && value && end.getTime() !== value.getTime()) {
        this.end = new Date(
          end.getFullYear(),
          end.getMonth(),
          end.getDate(),
          ...this.getTime(value)
        )
      }
    },
    selectedRange(value, prev) {
      if (!isEqual(value, prev)) {
        this.$emit("input", value)
      }
    },
    visible(value) {
      const start = this.start
      const end = this.end
      if (value && start && end) {
        const endYear = end.getFullYear()
        const endMonth = end.getMonth()
        const isInSameMonth = start.getFullYear() === endYear && start.getMonth() === endMonth
        this.startDate = start
        this.endDate = isInSameMonth ? new Date(endYear, endMonth + 1, 1) : end
      }

      if (value && (this.type === "date" || this.type === "date-time")) {
        this.startPanel = "date"
        this.endPanel = "date"
      }
    },
  },
  methods: {
    getTime(time) {
      return [time.getHours(), time.getMinutes(), time.getSeconds()]
    },
    handlePick(value) {
      if (this.start && this.end) {
        this.start = value
        this.end = ""
        return
      }

      if (!this.start && !this.end) {
        this.start = value
        return
      }

      if (this.start < value) {
        this.end = value
      } else {
        this.end = this.start
        this.start = value
      }
    },
    handleHover(value) {
      if (!this.selecting) {
        return
      }

      this.hover = value
    },
    handleYearPick(position, year) {
      this.setYear(position, year)

      if (this.type === "year") {
        const date = new Date(year, position === "start" ? 0 : 11, position === "start" ? 1 : 31)

        if (this.start && this.end) {
          this.start = ""
          this.end = ""
        }
        this[position] = date
      } else {
        this.switchpanel(position, "month")
      }
    },
    handleMonthPick(position, month) {
      this.setMonth(position, month)

      if (this.type === "month") {
        const date = new Date(
          this[`${position}Year`],
          month,
          position === "end" ? getDaysInMonth(this.endYear, month + 1) : 1
        )

        if (this.start && this.end) {
          this.start = ""
          this.end = ""
        }
        this[position] = date
      } else {
        this.switchpanel(position, "date")
      }
    },
    switchpanel(position, panel) {
      this[`${position}Panel`] = panel
    },
    handleClickPrev(position) {
      const panel = this[`${position}Panel`]
      let year = this[`${position}Year`]
      let month = this[`${position}Month`]

      if (panel === "date") {
        if (month === 0) {
          month = 11
          year = year - 1
        } else {
          month = month - 1
        }
      } else if (panel === "month") {
        year = year - 1
      } else if (panel === "year") {
        year = year - 10
      }

      this.setYear(position, year)
      this.setMonth(position, month)
    },
    handleClickNext(position) {
      const panel = this[`${position}Panel`]
      let year = this[`${position}Year`]
      let month = this[`${position}Month`]

      if (panel === "date") {
        if (month === 11) {
          month = 0
          year = year + 1
        } else {
          month = month + 1
        }
      } else if (panel === "month") {
        year = year + 1
      } else if (panel === "year") {
        year = year + 10
      }

      this.setYear(position, year)
      this.setMonth(position, month)
    },
    setYear(position, year) {
      this[`${position}Date`] = new Date(this[`${position}Date`].setYear(year))
    },
    setMonth(position, month) {
      this[`${position}Date`] = new Date(this[`${position}Date`].setMonth(month))
    },
    startDisabledDate(date) {
      const panel = this.startPanel
      let disabled = false
      if (panel === "year") {
        disabled = date.getFullYear() > this.endYear
      } else if (panel === "month") {
        disabled = date >= new Date(this.endYear, this.endMonth, 1)
      }

      return disabled || (this.disabledDate && this.disabledDate(date))
    },
    endDisabledDate(date) {
      const panel = this.endPanel
      let disabled = false
      if (panel === "year") {
        disabled = date.getFullYear() < this.startYear
      } else if (panel === "month") {
        const max = new Date(this.startYear, this.startMonth, 1)
        disabled = this.type === "month" ? date < max : date <= max
      }

      return disabled || (this.disabledDate && this.disabledDate(date))
    },
    handleSwitch() {
      if (this.selectedRange.length === 2) {
        const panel = this.startPanel === "date" ? "date-time" : "date"

        this.switchpanel("start", panel)
        this.switchpanel("end", panel)
      }
    },
    closePanel() {
      this.$emit("update:visible", false)
    },
  },
}
</script>
