import React, { useState, useEffect, useRef, useCallback } from "react";
import classNames from "classnames";
import moment, { Moment } from "moment";
import CalendarPart from "../calendar/CalendarPart";
import { ControlledProps, useDefaultValue } from "../form/controlled";
import { CalendarTable } from "../calendar/CalendarTable";
import { Input } from "../input/Input";
import { DropdownBox } from "../dropdown";
import { CommonDatePickerProps } from "./DatePickerProps";
import { useTranslation } from "../i18n";
import { CalendarTableType, DateChangeContext } from "../calendar/DateProps";
import { Popover } from "../popover/Popover";
import {
  DatePickerTrigger,
  isAfter,
  isBefore,
  isValidDate,
  isValidMonth,
} from "./util";
import { useDefault } from "../_util/use-default";
import { useConfig } from "../_util/config-context";
import { Icon } from "../icon";
import { noop } from "../_util/noop";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { mergeEventProps } from "../_util/merge-event-props";
import { KeyMap } from "../_util/key-map";

export interface MonthPickerProps
  extends CommonDatePickerProps,
    ControlledProps<Moment> {
  /**
   * 设置不可选月份，返回：
   *
   * - `true` 可选
   * - `false` 不可选
   *
   * @since 2.5.0
   */
  disabledMonth?: (date: Moment) => boolean;
}

export const MonthPicker = forwardRefWithStatics(
  function MonthPicker(
    props: MonthPickerProps,
    ref: React.Ref<HTMLDivElement>
  ) {
    const t = useTranslation(moment);
    const { classPrefix } = useConfig();
    const {
      header,
      className,
      style,
      value,
      onChange,
      onInputValueChange = noop,
      disabled,
      format = "YYYY-MM",
      placeholder = t.selectDate,
      defaultOpen = false,
      open,
      onOpenChange = noop,
      placement = "bottom-start",
      placementOffset = 5,
      closeOnScroll = true,
      escapeWithReference,
      popupContainer,
      overlayClassName,
      overlayStyle,
      clearable,
    } = useDefaultValue(props);

    const [hover, setHover] = useState<boolean>(false);

    // 当前面板类型
    const [type, setType] = useState<CalendarTableType>("month");

    // 当前面板展示时间
    const [curViewMoment, setCurViewMoment] = useState<Moment>(
      moment.isMoment(value) ? value : getDefaultViewMoment()
    );

    // 当前选中日期
    const [curValue, setCurValue] = useState<Moment>(value);

    // 选择器是否展开
    const [active, setActive] = useDefault(open, defaultOpen, onOpenChange);

    useEffect(() => {
      setCurValue(value);
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [active]);

    // 输入框显示值
    const inputRef = useRef<HTMLInputElement>(null);
    const getInputValue = useCallback(
      value => {
        return moment.isMoment(value)
          ? value.locale(t.locale).format(format)
          : "";
      },
      [t.locale, format]
    );
    const [inputValue, setInputValue] = useState<string>(
      getInputValue(curValue)
    );

    const syncInputValue = useCallback(
      (value: Moment) => {
        const inputValue = getInputValue(value);
        setInputValue(inputValue);
        onInputValueChange(inputValue, { valid: true });
      },
      // eslint-disable-next-line react-hooks/exhaustive-deps
      [getInputValue]
    );

    useEffect(() => {
      setCurValue(value);
      syncInputValue(value);
    }, [format, value, getInputValue, syncInputValue]);

    useEffect(() => {
      setCurViewMoment(curValue);
    }, [curValue]);

    function getDefaultViewMoment(): Moment {
      const { range } = props;
      const m = moment();
      // range 判断
      if (Array.isArray(range)) {
        let [start, end] = range;
        if (!moment.isMoment(start)) {
          start = moment(0);
        }
        if (!moment.isMoment(end)) {
          end = moment(2 ** 52);
        }
        if (isBefore(end, m, "year")) {
          return end;
        }
        if (isAfter(start, m, "year")) {
          return start;
        }
      }
      return m;
    }

    function handleChange(value: Moment, context: DateChangeContext) {
      setCurValue(value);
      syncInputValue(value);
      onChange(value, context);
      handleClose();
    }

    function handleOpen() {
      if (disabled) {
        return;
      }
      setActive(true);
      setType("month");
    }

    function handleClose() {
      syncInputValue(value);
      setActive(false);
    }

    function handleInputChange(content: string) {
      setInputValue(content);
      const value = moment(content, format, true);
      if (!value.isValid()) {
        onInputValueChange(content, { valid: false });
        return;
      }

      const valid = isValidMonth(value, props);
      if (valid) {
        setCurValue(value);
      }
      onInputValueChange(content, { valid: true });
    }

    function handleKeyDown(event: React.KeyboardEvent) {
      if (!active) {
        handleOpen();
        return;
      }
      switch (event.key) {
        case KeyMap.Enter:
          onChange(curValue, { event });
          handleClose();
          break;
        case KeyMap.Esc:
          handleClose();
          break;
      }
    }

    return (
      <Popover
        trigger={[
          DatePickerTrigger,
          { onOpen: handleOpen, onClose: handleClose },
        ]}
        visible={active}
        onVisibleChange={setActive}
        placement={placement}
        placementOffset={placementOffset}
        closeOnScroll={closeOnScroll}
        escapeWithReference={escapeWithReference}
        popupContainer={popupContainer}
        overlayClassName={overlayClassName}
        overlayStyle={overlayStyle}
        overlay={
          <DropdownBox preventMouseDown>
            <CalendarPart.Panel>
              {!!header && <CalendarPart.Header>{header}</CalendarPart.Header>}
              <CalendarTable
                {...props}
                monthOnly
                value={curValue}
                onChange={handleChange}
                type={type}
                onTypeChange={setType}
                curViewMoment={curViewMoment}
                onCurViewMomentChange={setCurViewMoment}
              />
            </CalendarPart.Panel>
          </DropdownBox>
        }
      >
        <div
          ref={ref}
          className={classNames(`${classPrefix}-monthpicker`, className)}
          style={style}
          {...mergeEventProps(props)}
        >
          <div
            className={classNames(`${classPrefix}-monthpicker__input`, {
              "is-hover": hover && value && clearable,
            })}
            onMouseEnter={() => setHover(true)}
            onMouseLeave={() => setHover(false)}
          >
            <Input
              ref={inputRef}
              disabled={disabled}
              placeholder={placeholder}
              value={inputValue}
              onChange={handleInputChange}
              onKeyDown={handleKeyDown}
              onClick={(event: React.MouseEvent) => {
                if (active) {
                  event.stopPropagation();
                }
              }}
            />
            {clearable && !disabled && (
              <Icon
                type="dismiss"
                onClick={event => {
                  event.stopPropagation();
                  onChange(null, { event });
                }}
              />
            )}
          </div>
        </div>
      </Popover>
    );
  },
  {
    defaultLabelAlign: "middle",
  }
);

MonthPicker.displayName = "MonthPicker";
