import React from "react";
import moment, { Moment } from "moment";
import Table, { CellStatus } from "./BaseTable";
import { useTranslation } from "../../i18n";
import {
  RangeDateType,
  CalendarTableType,
  DateChangeContext,
} from "../DateProps";
import { useConfig } from "../../_util/config-context";
import { noop } from "../../_util/noop";
import { getNow, isAfter, isBefore, isSame } from "../../datepicker/util";

export interface DateTableProps {
  /**
   * 当前展示日期
   */
  current: Moment;

  /**
   * 当前选中的日期
   */
  value: Moment;

  /**
   * 允许选择的时间范围限制
   */
  range?: RangeDateType;

  /**
   * 可选的日期范围
   */
  disabledDate?: (date: Moment) => boolean;

  /**
   * Cell 渲染状态
   */
  cellStatus?: (date: Moment) => CellStatus;

  /**
   * 选中回调
   */
  onSelect?: (value: Moment, context: DateChangeContext) => void;

  /**
   * type 变更回调
   */
  onTypeChange?: (type: CalendarTableType) => void;

  /**
   * 当前展示日期变更回调
   */
  onCurrentChange?: (
    current: Moment,
    context?: { from?: "jumper" | "outside-date" }
  ) => void;

  /**
   * 当前展示日期变更回调
   */
  onHoveredChange?: (hovered: Moment) => void;

  /**
   * 范围选择中日期的范围
   * 解决范围选择中相邻月间交错日期不可点的问题
   */
  dateRangeInRangePicker?: RangeDateType;
}

export function DateTable({
  current,
  onCurrentChange = noop,
  onHoveredChange = noop,
  range,
  disabledDate,
  onSelect = noop,
  onTypeChange = noop,
  cellStatus = () => CellStatus.Common,
  dateRangeInRangePicker,
}: DateTableProps) {
  const t = useTranslation();
  const { classPrefix } = useConfig();

  const month = current.month();
  const now = getNow(current);

  function genTable(): Moment[][] {
    const table = [];

    // 本月第一天
    // 从 current clone 保持 value 的时区等信息
    const first = moment(current).set({
      date: 1,
      hour: 0,
      minute: 0,
      second: 0,
      millisecond: 0,
    });
    const firstDay = first.day();

    // 第一周
    const firstWeek = Array<Moment>(7)
      .fill(null)
      .map((_, i) => first.clone().add(i - firstDay, "d"));

    table.push(firstWeek);

    let nextWeekFirst = firstWeek[6].clone().add(1, "d");
    while (nextWeekFirst.month() === month) {
      const week = Array<Moment>(7)
        .fill(null)
        .map((_, i) => nextWeekFirst.clone().add(i, "d")); // eslint-disable-line no-loop-func

      table.push(week);
      nextWeekFirst = week[6].clone().add(1, "d");
    }

    return table;
  }

  function renderYear() {
    return (
      <a className="op-item" onClick={() => onTypeChange("year")}>
        {current.format(t.yearFormat)}
      </a>
    );
  }

  function renderMonth() {
    return (
      <a className="op-item" onClick={() => onTypeChange("month")}>
        {moment.monthsShort(month)}
      </a>
    );
  }

  const [rangeMin, rangeMax] = range || [null, null];

  function isValidDate(date) {
    // 非 RangePicker
    if (typeof dateRangeInRangePicker === "undefined") {
      if (moment.isMoment(rangeMin) && isAfter(rangeMin, date, "day")) {
        return false;
      }
      if (moment.isMoment(rangeMax) && isBefore(rangeMax, date, "day")) {
        return false;
      }
    } else {
      // RangePicker
      const [aRangeMin, aRangeMax] = dateRangeInRangePicker;
      if (moment.isMoment(aRangeMin) && isAfter(aRangeMin, date, "day")) {
        return false;
      }
      if (moment.isMoment(aRangeMax) && isBefore(aRangeMax, date, "day")) {
        return false;
      }
    }
    return disabledDate(date);
  }

  return (
    <Table
      caption={
        t.monthBeforeYear ? (
          <>
            {renderMonth()}
            {renderYear()}
          </>
        ) : (
          <>
            {renderYear()}
            {renderMonth()}
          </>
        )
      }
      jumperOptions={{
        onNext: () =>
          onCurrentChange(moment(current).add(1, "month"), { from: "jumper" }),
        onPrev: () =>
          onCurrentChange(moment(current).subtract(1, "month"), {
            from: "jumper",
          }),
        onCurrent: () => onCurrentChange(now, { from: "jumper" }),
        isCurrent: isSame(current, now, "month"),
        nextDisabled:
          moment.isMoment(rangeMax) &&
          isBefore(
            rangeMax,
            moment(current)
              .set({
                date: 1,
              })
              .add(1, "month"),
            "day"
          ),
        prevDisabled:
          moment.isMoment(rangeMin) &&
          isAfter(
            rangeMin,
            moment(current)
              .set({
                date: 1,
              })
              .subtract(1, "day"),
            "day"
          ),
        nextTitle: t.nextMonth,
        prevTitle: t.prevMonth,
        curTitle: t.curMonth,
      }}
    >
      {/* 周 */}
      <div className={`${classPrefix}-calendar__header-group`}>
        <div className={`${classPrefix}-calendar__row`}>
          {moment.weekdaysMin().map(name => (
            <Table.Cell key={name} name={name} />
          ))}
        </div>
      </div>

      {genTable().map((week, i) => (
        <Table.Row key={`${month}-${i}`}>
          {week.map((date, j) => (
            <Table.Cell
              key={`${month}-${i}-${j}`}
              name={date.date()}
              value={date}
              onSelect={onSelect}
              status={cellStatus(date)}
              isNow={isSame(date, now, "day")}
              outside={date.month() !== month}
              disabled={!isValidDate(date)}
              onOutsideSelect={date =>
                onCurrentChange(date.clone(), { from: "outside-date" })
              }
              onHoveredChange={onHoveredChange}
            />
          ))}
        </Table.Row>
      ))}
    </Table>
  );
}

DateTable.displayName = "DateTable";
