<template>
  <div class="el-calendar" :class="{ 'el-calendar__simple': simple }">
    <template v-if="!simple">
      <div class="el-calendar__header" v-if="!$slots.header">
        <div class="el-calendar__title" v-if="!$slots.changeBtn">
          <div style="display: none">{{ i18nDate }}</div>
          <el-button-group v-if="showDaySel">
            <el-button type="plain" @click="toggleCheck('pre')"><i class="el-icon-arrow-left"></i></el-button>
            <el-button type="plain" @click="toggleCheck('next')"><i class="el-icon-arrow-right"></i></el-button>
          </el-button-group>
          <el-select v-model="year" placeholder="请选择" @change="change(3)" v-if="showYearSel">
            <el-option v-for="item in yearsOptions" :key="item" :label="item" :value="item">
            </el-option>
          </el-select>
          <el-select v-model="month" placeholder="请选择" @change="change(2)" v-if="showMonthSel">
            <el-option v-for="i in 12" :key="i" :label="i" :value="i">
            </el-option>
          </el-select>
          <!-- 1号是第一周 -->
          <el-select v-model="week" placeholder="请选择" @change="change(1)" v-if="showWeekSel">
            <el-option v-for="i in weeksOptions" :key="i.label" :label="i.label" :value="i.value">
            </el-option>
          </el-select>
          <el-button v-if="today" type="secondary" @click="selectDate('today')">
            {{ t("el.datepicker.today") }}
          </el-button>
        </div>
        <slot name="changeBtn"></slot>
        <slot name="title"></slot>
        <div class="el-calendar__button-group" v-if="validatedRange.length === 0 && !single">
          <el-radio-group v-model="selectType" @change="changeType" grouptype="linear" size="large">
            <template v-for="i in model.split('-')">
              <el-radio-button :label="Number(i)" :key="i">{{selectDateMap[i]}}</el-radio-button>
            </template>
          </el-radio-group>
          <slot name="right-title"></slot>
        </div>
      </div>
      <div class="el-calendar__header-slots" v-if="$slots.header">
        <slot name="header"></slot>
      </div>
      <!-- 日 -->
      <template v-if="selectType === 0">
        <date-table-day ref="dateDay" :date="date" :selected-day="realSelectedDay" :events="events" @clickItem="clickItem"></date-table-day>
      </template>
      <!-- 周 -->
      <template v-if="selectType === 1">
        <date-table-week ref="dateWeek" :weekIndex="week" :monthIndex="month" :events="events" :first-day-of-week="realFirstDayOfWeek" :festivals="festivals" @clickItem="clickItem">
        </date-table-week>
      </template>
      <!-- 月 -->
      <template v-if="selectType === 2">
        <div class="el-calendar__body" v-if="validatedRange.length === 0" role="no-range">
          <date-table ref="dateTable" :date="date" :selected-day="realSelectedDay" :first-day-of-week="firstDayOfWeek" :events="events"
            @pick="pickDay" :useMonthRange="useMonthRange" @clickItem="clickItem" />
        </div>
        <div v-else class="el-calendar__body" role="has-range">
          <date-table ref="dateTable" v-for="(range, index) in validatedRange" :key="index" :date="range[0]" :events="events"
            :selected-day="realSelectedDay" :range="range" :hide-header="index !== 0" :useMonthRange="useMonthRange"
            :first-day-of-week="firstDayOfWeek" @pick="pickDay" @clickItem="clickItem" />
        </div>
      </template>
      <!-- 年 -->
      <div v-if="selectType === 3" class="el-calendar__body el-calendar_month">
        <date-table-month ref="dateYear" v-for="i in dataArr" :key="i.month" :date="i.date" :activeDay="activeDay" @checkYearDate="checkYearDate"
          :first-day-of-week="firstDayOfWeek" @yearChange="yearChange">
          <template v-slot:yearCell="{date, data}">
            <slot name="yearCell" v-bind="{date, data}"></slot>
          </template>
        </date-table-month>
      </div>
    </template>

    <template v-if="simple">
      <!-- 简易日历 -->
      <simple-date ref="simple" :events="events" @changeToday="changeToday" @changeValue="changeValue" @changeSimpleDate="changeSimpleDate"
        :defaultValue="value" :defaultDate="defaultDate" :showDateHeader="showDateHeader">
        <template v-slot:dateCell="{ date, data }">
          <slot name="dateCell" v-bind="{ date, data }"></slot>
        </template>
      </simple-date>
    </template>
  </div>
</template>

<script>
import Locale from "modesign-ui/src/mixins/locale";
import fecha from "modesign-ui/src/utils/date";
import ElButton from "modesign-ui/packages/button";
import ElSelect from "modesign-ui/packages/select";
import ElOption from "modesign-ui/packages/option";
import ElButtonGroup from "modesign-ui/packages/button-group";
import ElRadioGroup from "modesign-ui/packages/radio-group";
import ElRadioButton from "modesign-ui/packages/radio-button";
import DateTable from "./date-table";
import DateTableDay from './date-table-day';
import DateTableMonth from "./date-table-month";
import DateTableWeek from "./date-table-week";
import SimpleDate from "./picker-date.vue";
import {
  validateRangeInOneMonth,
  getCurrentWeek,
  getWeeksNum,
} from "modesign-ui/src/utils/date-util";

const weekDays = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
];
const oneDay = 86400000;
const currentYear = new Date().getFullYear(); // 当前年份
const currentMonth = new Date().getMonth() + 1; // 当前月份
const currentWeek = getCurrentWeek(); // 当前周

const selectDateMap = {
  0: '日',
  1: '周',
  2: '月',
  3: '年'
}

export default {
  name: "ElCalendar",

  mixins: [Locale],

  components: {
    DateTable,
    DateTableDay,
    DateTableMonth,
    DateTableWeek,
    ElButton,
    ElSelect,
    ElOption,
    ElButtonGroup,
    ElRadioGroup,
    ElRadioButton,
    SimpleDate,
  },

  props: {
    value: [Date, String, Number],
    defaultDate: {},
    range: {
      type: Array,
      validator(range) {
        if (Array.isArray(range)) {
          return (
            range.length === 2 &&
            range.every(
              (item) =>
                typeof item === "string" ||
                typeof item === "number" ||
                item instanceof Date
            )
          );
        } else {
          return true;
        }
      },
    },
    firstDayOfWeek: {
      type: Number,
      default: 7, // 头部从周几开始 1是周一，7代表周日
    },
    // 年份下拉框
    yearsOptions: {
      type: Array,
      default: () => {
        const yearsOptions = []; // 默认前后15年
        for (let i = currentYear - 15; i < currentYear + 16; i++) {
          yearsOptions.push(i);
        }
        return yearsOptions;
      },
    },
    type: {
      // 0日 1周 2月 3年
      type: Number,
      default: 1,
    },
    events: {
      type: Array,
      default: () => [],
    },
    simple: {
      type: Boolean,
      default: false,
    },
    // 选择的年月周的类型
    model: {
      type: String,
      default: "0-1-2-3",
    },
    // 控制右上角周月年的展示
    single: {
      type: Boolean,
      default: false,
    },
    // 是否展示头部的今天按钮
    today: {
      type: Boolean,
      default: true
    },
    // 月事件范围是否和月份日历展示范围一致
    useMonthRange: {
      type: Boolean,
      default: false
    },
    // 节日列表
    festivals: {
      type: Array,
      default: () => {
        return []
      }
    },
    showDateHeader: {
      type: Boolean,
      default: false
    }
  },

  provide() {
    return {
      elCalendar: this,
    };
  },
  created() {
    this.changeTypeFunc(this.selectType);
  },
  methods: {
    toggleCheck(type) {
      const ms = type === 'pre' ? -24*60*60*1000 : 24*60*60*1000
      const date = (this.selectedDay ? new Date(this.selectedDay) : new Date).getTime() + ms
      const time = fecha.format(date, "yyyy-MM-dd")
      this.pickDay(time)
    },

    changeToday(date) {
      this.$emit("changeToday", date);
    },

    changeValue(date) {
      this.$emit("input", date);
      this.$emit("changeValue", date);
    },
    changeSimpleDate(date) {
      this.$emit('changeSimpleDate', date);
    },

    pickDay(day) {
      this.realSelectedDay = day;
      this.$emit('changeDate', day);
    },

    selectDate(type) {
      if (this.selectType === 3) {
        // 当前type是年，并且点击的是今天
        let day = this.formatedToday;
        this.year = currentYear;
        this.month = currentMonth;
        this.changeYearAndClearTip();
        this.$emit('changeDate', day)
      } else if (this.selectType === 2) {
        // 月
        this.showSelectDay();
        if (type === 'today') {
          let day = this.formatedToday;
          if (day === this.formatedDate) return;
          this.year = currentYear;
          this.month = currentMonth;
          this.pickDay(day);
        } else if (this.value) {
          let day = this.formatedDate;
          if (day === this.formatedDate) return;
          this.year = this.value.getFullYear();
          this.month = this.value.getMonth();
          this.pickDay(day);
        }
      } else if (this.selectType === 1) {
        // 周
        // TODO 从年、月切换到周和周日历里点击今天，处理方式应该不一致
        // if (type === 'today') {}
        this.month = currentMonth;
        this.week = currentWeek;
        this.$emit('changeDate', this.month, this.week);
      } else if (this.selectType === 0) {
        // 日
        this.showSelectDay();
        if (type === 'today') {
          let day = this.formatedToday;
          if (day === this.formatedDate) return;
          this.year = currentYear;
          this.month = currentMonth;
          this.pickDay(day);
        } else if (this.value) {
          let day = this.formatedDate;
          if (day === this.formatedDate) return;
          this.year = this.value.getFullYear();
          this.month = this.value.getMonth();
          this.pickDay(day);
        }
      }
    },

    toDate(val) {
      if (!val) {
        throw new Error("invalid val");
      }
      return val instanceof Date ? val : new Date(val);
    },

    rangeValidator(date, isStart) {
      const firstDayOfWeek = this.realFirstDayOfWeek;
      const expected = isStart
        ? firstDayOfWeek
        : firstDayOfWeek === 0
          ? 6
          : firstDayOfWeek - 1;
      const message = `${isStart ? "start" : "end"} of range should be ${weekDays[expected]
        }.`;
      if (date.getDay() !== expected) {
        console.warn(
          "[ElementCalendar]",
          message,
          "Invalid range will be ignored."
        );
        return false;
      }
      return true;
    },
    showSelectDay() {
      const realSelect = new Date(this.realSelectedDay);
      if (this.$refs.dateTable) {
        if (this.month === (realSelect.getMonth() + 1) && this.year === realSelect.getFullYear()) {
          this.$refs.dateTable.clickPickDay = true;
        } else {
          this.$refs.dateTable.clickPickDay = false;
        }
      }
    },
    // 下拉框切换展示日期
    change(type) {
      this.showSelectDay();
      // 3 年, 2月, 1周
      if (type === 3) {
        let month =  `00${this.month}`.slice(-2);
        let day = `${this.year}-${month}-01`;
        this.changeYearAndClearTip();
        this.pickDay(day);
      } else if (type === 2) {
        let month =  `00${this.month}`.slice(-2);
        let day = `${this.year}-${month}-01`;
        this.pickDay(day);
      } else {
        // do nothing
        this.$emit('changeDate', this.month, this.week);
      }
    },
    // 展示年日历时，清除点击标记，切换年份
    changeYearAndClearTip() {
      this.activeDay.month = null;
      this.activeDay.text = null;
      this.currentYear();
      return false;
    },
    // 切换按年展示还是按月、按周展示
    changeType(val) {
      this.$emit('changeType', val);
      this.changeTypeFunc(val);
    },
    changeTypeFunc(val) {
      if (val === 3) {
        this.showDaySel = false;
        this.showMonthSel = false;
        this.showWeekSel = false;
        this.showYearSel = true;
        this.changeYearAndClearTip();
      } else if (val === 2) {
        this.showDaySel = false;
        this.showMonthSel = true;
        this.showYearSel = true;
        this.showWeekSel = false;
        this.selectDate();
      } else if (val === 1) {
        // 周
        this.showDaySel = false;
        this.showMonthSel = true;
        this.showWeekSel = true;
        this.showYearSel = false;
        this.selectDate();
      } else if (val === 0) {
        this.showDaySel = true;
        this.showMonthSel = false;
        this.showWeekSel = false;
        this.showYearSel = false;
      }
    },
    // 获取当年的全部月份时间戳
    currentYear() {
      this.dataArr = [];
      for (let index = 0; index < 12; index++) {
        const date = new Date(this.year, index, 1);
        this.dataArr.push({
          month: index + 1,
          date,
        });
      }
    },
    yearChange(data) {
      this.year = this.year + data;
      this.currentYear();
    },

    checkYearDate(date) {
      this.$emit('checkYearDate', date);
    },

    // 点击事件
    clickItem(item, type) {
      this.$emit('clickEventItem', item, type)
    }
  },

  computed: {
    prevMonthDatePrefix() {
      const temp = new Date(this.date.getTime());
      temp.setDate(0);
      return fecha.format(temp, "yyyy-MM");
    },

    curMonthDatePrefix() {
      return fecha.format(this.date, "yyyy-MM");
    },

    nextMonthDatePrefix() {
      const temp = new Date(
        this.date.getFullYear(),
        this.date.getMonth() + 1,
        1
      );
      return fecha.format(temp, "yyyy-MM");
    },

    formatedDate() {
      return fecha.format(this.date, "yyyy-MM-dd");
    },

    i18nDate() {
      const year = this.date.getFullYear();
      const month = this.date.getMonth() + 1;
      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
      this.year = year;
      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
      this.month = month;
      return `${year} ${this.t("el.datepicker.year")} ${this.t(
        "el.datepicker.month" + month
      )}`;
    },

    formatedToday() {
      return fecha.format(this.now, "yyyy-MM-dd");
    },

    realSelectedDay: {
      get() {
        if (!this.value) return this.selectedDay;
        return this.formatedDate;
      },
      set(val) {
        this.selectedDay = val;
        const date = new Date(val);
        this.$emit("input", date);
      },
    },

    date() {
      if (!this.value) {
        if (this.realSelectedDay) {
          const d = this.selectedDay.split("-");
          return new Date(d[0], d[1] - 1, d[2]);
        } else if (this.validatedRange.length) {
          return this.validatedRange[0][0];
        }
        return this.now;
      } else {
        return this.toDate(this.value);
      }
    },

    // if range is valid, we get a two-digit array
    validatedRange() {
      let range = this.range;
      if (!range) return [];
      range = range.reduce((prev, val, index) => {
        const date = this.toDate(val);
        if (this.rangeValidator(date, index === 0)) {
          prev = prev.concat(date);
        }
        return prev;
      }, []);
      if (range.length === 2) {
        const [start, end] = range;
        if (start > end) {
          console.warn(
            "[ElCalendar]end time should be greater than start time"
          );
          return [];
        }
        // start time and end time in one month
        if (validateRangeInOneMonth(start, end)) {
          return [[start, end]];
        }
        const data = [];
        let startDay = new Date(start.getFullYear(), start.getMonth() + 1, 1);
        const lastDay = this.toDate(startDay.getTime() - oneDay);
        if (!validateRangeInOneMonth(startDay, end)) {
          console.warn(
            "[ElCalendar]start time and end time interval must not exceed two months"
          );
          return [];
        }
        // 第一个月的时间范围
        data.push([start, lastDay]);
        // 下一月的时间范围，需要计算一下该月的第一个周起始日
        const firstDayOfWeek = this.realFirstDayOfWeek;
        const nextMontFirstDay = startDay.getDay();
        let interval = 0;
        if (nextMontFirstDay !== firstDayOfWeek) {
          if (firstDayOfWeek === 0) {
            interval = 7 - nextMontFirstDay;
          } else {
            interval = firstDayOfWeek - nextMontFirstDay;
            interval = interval > 0 ? interval : 7 + interval;
          }
        }
        startDay = this.toDate(startDay.getTime() + interval * oneDay);
        if (startDay.getDate() < end.getDate()) {
          data.push([startDay, end]);
        }
        return data;
      }
      return [];
    },

    realFirstDayOfWeek() {
      if (this.firstDayOfWeek < 1 || this.firstDayOfWeek > 6) {
        return 0;
      }
      return Math.floor(this.firstDayOfWeek);
    },

    weeksOptions() {
      const month = this.month ? this.month - 1 : currentMonth;
      return new Array(getWeeksNum(this.month - 1)).fill().map((x, i) => {
        return { label: `第${i + 1}周`, value: i + 1 };
      });
    }
  },

  watch: {
    type(newVal) {
      this.selectType = Number(newVal);
      this.changeTypeFunc(newVal);
    }
  },
  data() {
    return {
      selectDateMap,
      selectedDay: "",
      now: new Date(),
      year: currentYear,
      month: currentMonth,
      week: currentWeek,
      dataArr: [],
      showMonthSel: true,
      showWeekSel: true,
      showYearSel: true,
      showDaySel: true,
      selectType: Number(this.type),
      activeDay: {
        month: null,
        text: null,
      },
    };
  },

  mounted() {
    if (this.simple) {
      this.$refs.simple.defaultValue = this.formatedToday;
    }
  },
};
</script>
