import classnames from "classnames";
import dayjs from "dayjs";
import React from "react";
import Taro, { Events } from "@tarojs/taro";

import { Swiper, SwiperItem, View, Image } from "@tarojs/components";
import {
  BaseEventOrig,
  ITouch,
  ITouchEvent,
} from "@tarojs/components/types/common";
import {
  AtCalendarBodyListGroup,
  AtCalendarBodyProps,
  AtCalendarBodyState,
  Calendar,
} from "../../../../types/calendar";
import { delayQuerySelector } from "../../../common/utils";
import generateCalendarGroup from "../common/helper";
import AtCalendarDateList from "../ui/date-list/index";
import AtCalendarDayList from "../ui/day-list/index";

import "./isFolding.less";

const ANIMTE_DURATION = 300;

const defaultProps: Partial<AtCalendarBodyProps> = {
  marks: [],
  selectedDate: {
    end: Date.now(),
    start: Date.now(),
  },
  format: "YYYY-MM-DD",
  generateDate: Date.now(),
};

// const events = new Events();

export default class AtCalendarBody extends React.Component<any, any> {
  static defaultProps: Partial<AtCalendarBodyProps> = defaultProps;

  public constructor(props: any) {
    super(props);
    const {
      validDates,
      marks,
      format,
      minDate,
      maxDate,
      generateDate,
      selectedDate,
      selectedDates,
      currentDate,
    } = props;

    this.generateFunc = generateCalendarGroup({
      validDates,
      format,
      minDate,
      maxDate,
      marks,
      selectedDates,
    });
    // const listGroup = this.getGroups(generateDate, selectedDate);
    const listGroup = this.getGroups(currentDate, selectedDate);
    this.state = {
      listGroup,
      offsetSize: 0,
      isAnimate: false,
      isFolding: false,
      temporaryCalendar: [],
      temporaryCalendarWeek: [],
    };
  }

  public componentDidMount(): void {
    delayQuerySelector(".at-calendar-slider__main").then((res: any) => {
      if (res.length) {
        this.maxWidth = res[0]?.width;
      }
    });

    this.onChangeFoldingCalendar();
  }

  public UNSAFE_componentWillReceiveProps(
    nextProps: AtCalendarBodyProps
  ): void {
    const {
      validDates,
      marks,
      format,
      minDate,
      maxDate,
      generateDate,
      selectedDate,
      selectedDates,
    } = nextProps;

    this.generateFunc = generateCalendarGroup({
      validDates,
      format,
      minDate,
      maxDate,
      marks,
      selectedDates,
    });
    const listGroup = this.getGroups(generateDate, selectedDate);

    this.setState({
      offsetSize: 0,
      listGroup,
    });
  }

  private changeCount = 0;
  private currentSwiperIndex = 1;
  private startX = 0;
  private swipeStartPoint = 0;
  private isPreMonth = false;
  private maxWidth = 0;
  private isTouching = false;

  private generateFunc: (
    generateDate: number,
    selectedDate: Calendar.SelectedDate,
    isShowStatus?: boolean
  ) => Calendar.ListInfo<Calendar.Item>;

  private getGroups = (
    generateDate: number,
    selectedDate: Calendar.SelectedDate
  ): AtCalendarBodyListGroup => {
    const dayjsDate = dayjs(generateDate);
    const arr: AtCalendarBodyListGroup = [];
    const preList: Calendar.ListInfo<Calendar.Item> = this.generateFunc(
      dayjsDate.subtract(1, "month").valueOf(),
      selectedDate
    );

    const nowList: Calendar.ListInfo<Calendar.Item> = this.generateFunc(
      generateDate,
      selectedDate,
      true
    );

    const nextList: Calendar.ListInfo<Calendar.Item> = this.generateFunc(
      dayjsDate.add(1, "month").valueOf(),
      selectedDate
    );

    const preListIndex =
      this.currentSwiperIndex === 0 ? 2 : this.currentSwiperIndex - 1;
    const nextListIndex =
      this.currentSwiperIndex === 2 ? 0 : this.currentSwiperIndex + 1;

    arr[preListIndex] = preList;
    arr[nextListIndex] = nextList;
    arr[this.currentSwiperIndex] = nowList;

    return arr;
  };

  private handleTouchStart = (e: ITouchEvent): void => {
    if (!this.props.isSwiper) {
      return;
    }
    this.isTouching = true;
    this.startX = e.touches[0].clientX;
  };

  private handleTouchMove = (e: ITouchEvent): void => {
    if (!this.props.isSwiper) {
      return;
    }
    if (!this.isTouching) return;

    const { clientX } = e.touches[0];
    const offsetSize = clientX - this.startX;

    this.setState({
      offsetSize,
    });
  };

  private animateMoveSlide = (offset: number, callback?: () => void): void => {
    this.setState(
      {
        isAnimate: true,
      },
      () => {
        this.setState({
          offsetSize: offset,
        });
        setTimeout(() => {
          this.setState(
            {
              isAnimate: false,
            },
            () => {
              callback?.();
            }
          );
        }, ANIMTE_DURATION);
      }
    );
  };

  private handleTouchEnd = (): void => {
    if (!this.props.isSwiper) {
      return;
    }

    const { offsetSize } = this.state;

    this.isTouching = false;
    const isRight = offsetSize > 0;

    const breakpoint = this.maxWidth / 2;
    const absOffsetSize = Math.abs(offsetSize);

    if (absOffsetSize > breakpoint) {
      const res = isRight ? this.maxWidth : -this.maxWidth;
      return this.animateMoveSlide(res, () => {
        this.props.onSwipeMonth(isRight ? -1 : 1);
      });
    }
    this.animateMoveSlide(0);
  };

  private handleChange = (
    e: BaseEventOrig<{
      current: number;
      source: string;
    }>
  ): void => {
    const { current, source } = e.detail;

    if (source === "touch") {
      this.currentSwiperIndex = current;
      this.changeCount += 1;
    }
  };

  private handleAnimateFinish = (): void => {
    if (this.changeCount > 0) {
      this.props.onSwipeMonth(
        this.isPreMonth ? -this.changeCount : this.changeCount
      );
      this.changeCount = 0;
    }
  };

  private handleSwipeTouchStart = (
    e: ITouchEvent & { changedTouches: Array<ITouch> }
  ): void => {
    const { clientY, clientX } = e.changedTouches[0];
    this.swipeStartPoint = this.props.isVertical ? clientY : clientX;
  };

  private handleSwipeTouchEnd = (
    e: ITouchEvent & { changedTouches: Array<ITouch> }
  ): void => {
    const { clientY, clientX } = e.changedTouches[0];
    this.isPreMonth = this.props.isVertical
      ? clientY - this.swipeStartPoint > 0
      : clientX - this.swipeStartPoint > 0;
  };

  // 关键代码：收起展开改变界面
  private onChangeFoldingCalendar = () => {
    let { isFolding, listGroup } = this.state;
    let { complete, incomplete } = this.props;
    isFolding = !isFolding;
    if (isFolding) {
      let toDay = dayjs().format("YYYY-MM-DD");
      let selectedDay = listGroup[1].list.find(
        (item) => item.isSelected
      )?.value;
      let startWeekDay = dayjs(selectedDay)
        .startOf("week")
        .add(1, "day")
        .format("YYYY-MM-DD");
      let temporaryCalendar = [0, 1, 2, 3, 4, 5, 6].map((dayIndex) => {
        let currentItemDay = dayjs(startWeekDay)
          .add(dayIndex, "day")
          .format("YYYY-MM-DD");

        let isComplete = false;
        let isInComplete = false;
        if (complete.length) {
          // 已完成日期
          isComplete = complete.includes(currentItemDay);
        }
        if (incomplete.length) {
          // 未完成日期
          isInComplete = incomplete.includes(currentItemDay);
        }
        return {
          isDisabled: false,
          isSelected: currentItemDay === selectedDay,
          isSelectedHead: currentItemDay === selectedDay,
          isSelectedTail: currentItemDay === selectedDay,
          isToday: toDay === currentItemDay,
          isComplete: isComplete,
          isInComplete: isInComplete,
          marks: [],
          text: Number(dayjs(currentItemDay).format("DD")),
          type: 0,
          value: currentItemDay,
        };
      });
      this.setState({ temporaryCalendar });
    } else {
      let temporaryCalendar = listGroup[1].list.map((item: any) => {
        let isComplete = false;
        let isInComplete = false;
        let toDay = dayjs().format("YYYY-MM-DD");
        if (complete.length) {
          // 已完成日期
          isComplete = complete.includes(item.value);
        }
        if (incomplete.length) {
          // 未完成日期
          isInComplete = incomplete.includes(item.value);
        }
        return {
          ...item,
          isComplete,
          isInComplete,
          isToday: toDay === item.value,
        };
      });
      this.setState({ temporaryCalendar });
    }
    this.setState({ isFolding });
  };

  onDayClick(item: any) {
    let { temporaryCalendar } = this.state;
    let oldValueItem = temporaryCalendar.findIndex((ele) => ele.isSelected);
    let newValueItem = temporaryCalendar.findIndex(
      (ele) => ele.value === item.value
    );

    temporaryCalendar[oldValueItem].isSelected = false;
    temporaryCalendar[oldValueItem].isSelectedHead = false;
    temporaryCalendar[oldValueItem].isSelectedTail = false;

    temporaryCalendar[newValueItem].isSelected = true;
    temporaryCalendar[newValueItem].isSelectedHead = true;
    temporaryCalendar[newValueItem].isSelectedTail = true;

    this.setState({ temporaryCalendar });
    this.props.onDayClick(item);
  }

  // 关键代码：修改日历-重新渲染
  onSelectDataChange(value: any) {
    let { complete, incomplete } = this.props;
    let { temporaryCalendar } = this.state;

    let newDateValue = dayjs(value).valueOf();
    const listGroup = this.getGroups(newDateValue, {
      start: newDateValue,
      end: newDateValue,
    });
    temporaryCalendar = JSON.parse(JSON.stringify(listGroup[1].list));
    temporaryCalendar = temporaryCalendar.map((item: any) => {
      let isComplete = false;
      let isInComplete = false;
      let toDay = dayjs().format("YYYY-MM-DD");
      if (complete.length) {
        // 已完成日期
        isComplete = complete.includes(item.value);
      }
      if (incomplete.length) {
        // 未完成日期
        isInComplete = incomplete.includes(item.value);
      }
      return {
        ...item,
        isComplete,
        isInComplete,
        isToday: toDay === item.value,
      };
    });

    this.setState({ listGroup, temporaryCalendar, isFolding: false });

    let newValueItemIndex = temporaryCalendar.findIndex(
      (ele) => ele.value === value
    );
    this.props.onDayClick(temporaryCalendar[newValueItemIndex]);
  }

  public render(): JSX.Element {
    const { isSwiper } = this.props;
    const { isFolding, isAnimate, offsetSize, listGroup, temporaryCalendar } =
      this.state;

    Taro.eventCenter.on(
      "onSelectDataChange",
      this.onSelectDataChange.bind(this)
    );

    if (!isSwiper) {
      return (
        <View
          className={classnames(
            "main",
            "at-calendar-slider__main",
            `at-calendar-slider__main--${process.env.TARO_ENV}`
          )}
        >
          <AtCalendarDayList />
          <View
            className={classnames("main__body", "body", {
              "main__body--isFolding": isFolding,
            })}
          >
            <View className="body__slider body__slider--now">
              {/* <AtCalendarDateList
                list={temporaryCalendar}
                onClick={this.onDayClick.bind(this)}
                onLongClick={this.props.onLongClick}
              /> */}
              {/* 仅关注此处定制数据即可 */}
              <AtCalendarDateList list={temporaryCalendar} />
            </View>
          </View>
          <View className="arrow_box" onClick={this.onChangeFoldingCalendar}>
            <Image
              className={classnames("arrow", {
                "arrow-rotate": isFolding,
              })}
              src={require("../img/arrow-down.png")}
              mode="aspectFill"
            />
          </View>
        </View>
      );
    }

    /* 需要 Taro 组件库维护 Swiper 使 小程序 和 H5 的表现保持一致  */
    if (process.env.TARO_ENV === "h5") {
      return (
        <View
          className={classnames(
            "main",
            "at-calendar-slider__main",
            `at-calendar-slider__main--${process.env.TARO_ENV}`
          )}
          onTouchEnd={this.handleTouchEnd}
          onTouchMove={this.handleTouchMove}
          onTouchStart={this.handleTouchStart}
        >
          <AtCalendarDayList />
          <View
            className={classnames("main__body  body", {
              "main__body--slider": isSwiper,
              "main__body--animate": isAnimate,
            })}
            style={{
              transform: isSwiper
                ? `translateX(-100%) translate3d(${offsetSize},0,0)`
                : "",
              WebkitTransform: isSwiper
                ? `translateX(-100%) translate3d(${offsetSize}px,0,0)`
                : "",
            }}
          >
            <View className="body__slider body__slider--pre">
              <AtCalendarDateList list={listGroup[0].list} />
            </View>
            <View className="body__slider body__slider--now">
              <AtCalendarDateList
                list={temporaryCalendar}
                onClick={this.props.onDayClick}
                onLongClick={this.props.onLongClick}
              />
            </View>
            <View className="body__slider body__slider--next">
              <AtCalendarDateList list={listGroup[2].list} />
            </View>
          </View>
        </View>
      );
    }

    return (
      <View
        className={classnames(
          "main",
          "at-calendar-slider__main",
          `at-calendar-slider__main--${process.env.TARO_ENV}`
        )}
      >
        <AtCalendarDayList />
        <Swiper
          circular
          current={1}
          skipHiddenItemLayout
          className={classnames("main__body", {
            "main__body--isFolding": isFolding,
          })}
          onChange={this.handleChange}
          vertical={this.props.isVertical}
          onAnimationFinish={this.handleAnimateFinish}
          onTouchEnd={this.handleSwipeTouchEnd}
          onTouchStart={this.handleSwipeTouchStart}
        >
          {listGroup.map((item, key) => (
            <SwiperItem key={key} itemId={key.toString()}>
              <AtCalendarDateList
                list={key === 1 ? temporaryCalendar : item.list}
                onClick={this.onDayClick.bind(this)}
                onLongClick={this.props.onLongClick}
              />
              {/* <AtCalendarDateList
                list={item.list}
                onClick={this.props.onDayClick}
                onLongClick={this.props.onLongClick}
              /> */}
            </SwiperItem>
          ))}
        </Swiper>

        <View className="arrow_box" onClick={this.onChangeFoldingCalendar}>
          <Image
            className={classnames("arrow", {
              "arrow-rotate": isFolding,
            })}
            src={require("../img/arrow-down.png")}
            mode="aspectFill"
          />
        </View>
      </View>
    );
  }
}
