import React, { useReducer, useCallback, useMemo } from 'react';
import classnames from 'classnames';
import dayjs from 'dayjs';
import { formatNumber } from '@jy-fe/utils';
import CalendarHeader from './xui-calendar-head';
import CalendarBody from './xui-calendar-body';
import CalendarFooter from './xui-calendar-footer';
import { CalendarProps, InitialStateType } from './xui-calendar.d';

const displayDaysPerMonth = (year: number) => {
  // 定义每个月的天数，如果是闰年第二月改为29天
  const daysInMonth: number[] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
    daysInMonth[1] = 29;
  }

  // 以下为了获取一年中每一个月在日历选择器上显示的数据，
  // 从上个月开始，接着是当月，最后是下个月开头的几天

  // 定义一个数组，保存上一个月的天数
  const daysInPreviousMonth: number[] = ([] as number[]).concat(daysInMonth);
  daysInPreviousMonth.unshift(daysInPreviousMonth.pop() || 31);

  // 获取每一个月显示数据中需要补足上个月的天数
  const addDaysFromPreMonth = new Array(12).fill(null).map((item, index) => {
    const day = new Date(year, index, 1).getDay();
    if (day === 0) {
      return 6;
    }
    return day - 1;
  });

  // 已数组形式返回一年中每个月的显示数据,每个数据为6行*7天
  return new Array(12).fill([]).map((month, monthIndex) => {
    let addDays = addDaysFromPreMonth[monthIndex];
    const daysCount = daysInMonth[monthIndex];
    let daysCountPrevious = daysInPreviousMonth[monthIndex];
    const monthData: number[] = [];
    // 补足上一个月
    for (; addDays > 0; addDays--) {
      monthData.unshift(daysCountPrevious--);
    }
    // 添入当前月
    for (let i = 0; i < daysCount; i++) {
      monthData.push(i + 1);
    }
    // 补足下一个月
    for (let i = 42 - monthData.length, j = 0; j < i; j++) {
      monthData.push(j + 1);
    }
    return monthData;
  });
};

const monthArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const monthMap = {
  1: '一月',
  2: '二月',
  3: '三月',
  4: '四月',
  5: '五月',
  6: '六月',
  7: '七月',
  8: '八月',
  9: '九月',
  10: '十月',
  11: '十一月',
  12: '十二月',
};

const now = new Date();

const initialState: InitialStateType = {
  yearRangeIndex: 0,
  year: now.getFullYear(),
  month: now.getMonth(),
  day: now.getDate(),
  dates: [],
  selectYear: false,
  selectMonth: false,
  showYearQuickSelect: false,
  showMonthQuickSelect: false,
};

const reducer = (
  state: InitialStateType,
  action: {
    type: string;
    payload?: { [key: string]: any };
  },
) => {
  switch (action.type) {
    case 'update':
      return { ...state, ...action.payload };
    default:
      throw new Error();
  }
};

const Index: React.FC<CalendarProps> = ({
  className: wrapperClassName = '',
  style = {},
  defaultDates = [],
  selectYear: selectYearProp = true,
  selectMonth: selectMonthProp = true,
  multiple = false,
  minDate = '',
  maxDate = '',
  onDayClick,
}) => {
  /** 格式化dates时间格式 */
  const datesFormat = useCallback((dates: (string | number)[] | undefined) => {
    const formatDates: string[] = [];
    if (dates && dates.length > 0) {
      dates.forEach((date: string | number) => {
        if (typeof date === 'string') {
          formatDates.push(dayjs(date).format('YYYY-MM-DD'));
        }
        if (typeof date === 'number') {
          formatDates.push(dayjs(date * 1000).format('YYYY-MM-DD'));
        }
      });
    }
    return formatDates;
  }, []);

  const [state, hookDispatch] = useReducer(reducer, {
    ...initialState,
    dates: datesFormat(defaultDates),
    selectYear: selectYearProp,
    selectMonth: selectMonthProp,
  } as InitialStateType);

  const {
    yearRangeIndex,
    year,
    month,
    day,
    dates,
    selectYear,
    selectMonth,
    showYearQuickSelect,
    showMonthQuickSelect,
  } = state;

  const triggerDispatch = useCallback(
    (payload: Partial<InitialStateType>) => {
      hookDispatch({
        type: 'update',
        payload,
      });
    },
    [state],
  );

  /** 处理返回的字符串dateString */
  const dateToString = useCallback((currentDates: string[]) => {
    const newDates = currentDates.sort();

    const diffArr: string[][] = [];
    let diffIndex = 0;
    let newIndex = 1;

    newDates.forEach((item, i) => {
      if (Number(i) === 0) {
        diffArr[diffIndex] = [item, item];
        return;
      }

      const d = new Date(item);
      const newDate = d.setDate(d.getDate() - newIndex);
      if (
        dayjs(dayjs(newDate).format('YYYY-MM-DD')).unix() === dayjs(diffArr[diffIndex][0]).unix()
      ) {
        diffArr[diffIndex][1] = item;
        newIndex += 1;
        return;
      }

      diffIndex += 1;
      diffArr[diffIndex] = [item, item];
      newIndex = 1;
    });

    const formatMD = (d: string) => dayjs(d).format('MM-DD');

    const dateString = diffArr
      .map((item: string[]) => {
        if (item[0] === item[1]) {
          return formatMD(item[0]);
        }
        return `${formatMD(item[0])}至${formatMD(item[1])}`;
      })
      .join(',');

    return dateString;
  }, []);

  /** 触发onDayClick */
  const recordDatesCallback = useCallback((currentDates: string[]) => {
    const dateString = dateToString(currentDates);
    if (onDayClick) {
      onDayClick({ dates: currentDates, dateString });
    }
  }, []);

  /** 记录所有选中的日期 */
  const recordDates = useCallback(
    (dateString: string) => {
      let currentDates = dates.slice();
      if (multiple) {
        if (currentDates.indexOf(dateString) > -1) {
          currentDates.splice(currentDates.indexOf(dateString), 1);
        } else {
          currentDates.push(dateString);
        }
      } else {
        currentDates = [dateString];
      }
      triggerDispatch({
        dates: currentDates,
      });
      recordDatesCallback(currentDates);
    },
    [multiple, dates],
  );

  /** 点击年份快速选择 */
  const handleSelectYear = useCallback(() => {
    triggerDispatch({
      showYearQuickSelect: !showYearQuickSelect,
      yearRangeIndex: 0,
    });
  }, [showYearQuickSelect]);

  /** 点击月份快速选择 */
  const handleSelectMonth = useCallback(() => {
    triggerDispatch({
      showMonthQuickSelect: !showMonthQuickSelect,
      yearRangeIndex: 0,
    });
  }, [showMonthQuickSelect]);

  /** 选择年份 */
  const changeYear = useCallback((currentYear: number) => {
    triggerDispatch({
      year: currentYear,
      showYearQuickSelect: false,
      yearRangeIndex: 0,
    });
  }, []);

  /** 选择月份 */
  const changeMonth = useCallback((currentMonth: number) => {
    triggerDispatch({
      month: currentMonth,
      showMonthQuickSelect: false,
    });
  }, []);

  /** 选择日期 */
  const datePick = useCallback(
    (currentDay: number, currentYear?: number, currentMonth?: number) => {
      triggerDispatch({
        day: currentDay,
      });
      const dateString = `${currentYear || year}-${formatNumber(
        (currentMonth || currentMonth === 0 ? currentMonth : month) + 1,
      )}-${formatNumber(currentDay)}`;
      recordDates(dateString);
    },
    [year, month, dates],
  );

  /** 切换上一年范围 */
  const prevYearQuick = useCallback(() => {
    triggerDispatch({
      yearRangeIndex: yearRangeIndex - 1,
    });
  }, [yearRangeIndex]);

  /** 切换下一年范围 */
  const nextYearQuick = useCallback(() => {
    triggerDispatch({
      yearRangeIndex: yearRangeIndex + 1,
    });
  }, [yearRangeIndex]);

  /** 切换到上一年 */
  const prevYear = useCallback(() => {
    triggerDispatch({
      year: year - 1,
    });
  }, [year]);

  /** 切换到下一年 */
  const nextYear = useCallback(() => {
    triggerDispatch({
      year: year + 1,
    });
  }, [year]);

  /** 切换上一个月 */
  const prevMonth = useCallback(
    (currentDay?: number) => {
      let currentYear = year;
      let currentMonth = month;
      if (month === 0) {
        currentYear = year - 1;
        currentMonth = 11;
        triggerDispatch({
          year: currentYear,
          month: currentMonth,
        });
      } else {
        currentMonth = month - 1;
        triggerDispatch({
          month: currentMonth,
        });
      }
      if (currentDay || currentDay === 0) {
        datePick(currentDay, currentYear, currentMonth);
      }
    },
    [year, month],
  );

  /** 切换下一个月 */
  const nextMonth = useCallback(
    (currentDay?: number) => {
      let currentYear = year;
      let currentMonth = month;
      if (month === 11) {
        currentYear = year + 1;
        currentMonth = 0;
        triggerDispatch({
          year: currentYear,
          month: currentMonth,
        });
      } else {
        currentMonth = month + 1;
        triggerDispatch({
          month: currentMonth,
        });
      }
      if (currentDay || currentDay === 0) {
        datePick(currentDay, currentYear, currentMonth);
      }
    },
    [year, month],
  );

  /** 获取年份选择区间 */
  const getYearSelectRange = useMemo(() => {
    const rangeStartYear = Math.floor(year / 12) * 12 + yearRangeIndex * 12;
    const yearArray: { index: number; year: number; isSelect: boolean }[] = [];
    for (let i = 0; i < 12; i++) {
      yearArray.push({
        index: i,
        year: rangeStartYear + i,
        isSelect: rangeStartYear + i === year,
      });
    }
    return yearArray;
  }, [year, yearRangeIndex]);

  const props = {
    minDate,
    maxDate,
    viewData: displayDaysPerMonth(year),
    datePicked: `${year} 年
                 ${month + 1} 月
                 ${day} 日`,
  };
  const className = 'xui-ant-calendar';
  const yearArray = getYearSelectRange;

  return (
    <div
      className={classnames({
        [className]: true,
        [wrapperClassName || '']: wrapperClassName,
      })}
      style={style}
    >
      {showYearQuickSelect && (
        <div className={`${className}--yearSelect`}>
          {yearArray.map((item: { index: number; year: number; isSelect: boolean }) => (
            <span className={`${className}--yearSelect--yearTd`} key={item.index}>
              <span
                className={classnames(
                  `${className}--yearSelect--yearTd--yearItem`,
                  item.isSelect ? `${className}--yearSelect--yearTd--yearItem--active` : '',
                )}
                onClick={() => changeYear(item.year)}
              >
                {item.year}
              </span>
            </span>
          ))}
        </div>
      )}
      {showMonthQuickSelect && (
        <div className={`${className}--monthSelect`}>
          {monthArray.map((item: number) => (
            <span className={`${className}--monthSelect--monthTd`} key={item}>
              <span
                className={classnames(
                  `${className}--monthSelect--monthTd--monthItem`,
                  month + 1 === item ? `${className}--monthSelect--monthTd--monthItem--active` : '',
                )}
                onClick={() => changeMonth(item - 1)}
              >
                {monthMap[item]}
              </span>
            </span>
          ))}
        </div>
      )}
      <div className={`${className}--main`}>
        <CalendarHeader
          prevYearQuick={prevYearQuick}
          prevYear={prevYear}
          prevMonth={prevMonth}
          nextYearQuick={nextYearQuick}
          nextYear={nextYear}
          nextMonth={nextMonth}
          selectYear={selectYear}
          selectMonth={selectMonth}
          onSelectYear={handleSelectYear}
          onSelectMonth={handleSelectMonth}
          showYearQuickSelect={showYearQuickSelect}
          showMonthQuickSelect={showMonthQuickSelect}
          year={year}
          month={month}
        />
        <CalendarBody
          {...props}
          prevMonth={prevMonth}
          nextMonth={nextMonth}
          datePick={datePick}
          year={year}
          month={month}
          dates={dates}
        />
        <CalendarFooter />
      </div>
    </div>
  );
};

export default Index;
