import React, { useState, useEffect, useCallback } from "react";
import classNames from "classnames";
import moment, { Moment } from "moment";
import CalendarPart from "../calendar/CalendarPart";
import { useDefaultValue } from "../form/controlled";
import { Input } from "../input";
import { TimeTable } from "./TimeTable";
import { Button } from "../button";
import { useTranslation } from "../i18n";
import { DropdownBox } from "../dropdown";
import { TimePickerProps } from "./TimeProps";
import { genShowHourMinuteSecond, isValidTimeValue } from "./util";
import { TimeRangePicker } from "./TimeRangePicker";
import { useDefault } from "../_util/use-default";
import { Popover } from "../popover/Popover";
import { DatePickerTrigger } from "../datepicker/util";
import { useConfig } from "../_util/config-context";
import { noop } from "../_util/noop";
import { KeyMap } from "../_util/key-map";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { mergeEventProps } from "../_util/merge-event-props";
import { Icon } from "../icon";

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

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

    const [curValue, setCurValue] = useState<Moment>(value);
    const [active, setActive] = useDefault(open, defaultOpen, onOpenChange);

    const { showMilliseconds } = genShowHourMinuteSecond(format);

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

    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, getInputValue, syncInputValue, value]);

    function handleInputChange(content: string) {
      setInputValue(content);

      // 格式校验
      const value = moment(content, format, true);
      if (!value.isValid()) {
        onInputValueChange(content, { valid: false });
        return;
      }
      const valid = isValidTimeValue(value, props);
      if (valid) {
        setCurValue(value);
      }
      onInputValueChange(content, { valid });
    }

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

    function handleSelectChange(value: Moment) {
      syncInputValue(value);
      setCurValue(value);
    }

    function handleOk(event: React.SyntheticEvent) {
      if (!curValue) {
        return;
      }
      onChange(curValue, { event });
      handleClose();
    }

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

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

    return (
      <Popover
        trigger={[
          DatePickerTrigger,
          { onOpen: handleOpen, onClose: handleClose },
        ]}
        visible={active}
        onVisibleChange={visible => {
          if (!visible) {
            handleClose();
          } else {
            setActive(visible);
          }
        }}
        placement={placement}
        placementOffset={placementOffset}
        closeOnScroll={closeOnScroll}
        escapeWithReference={escapeWithReference}
        popupContainer={popupContainer}
        overlayClassName={overlayClassName}
        overlayStyle={overlayStyle}
        overlay={
          <DropdownBox preventMouseDown>
            <CalendarPart.Panel timeMode>
              <CalendarPart.Body>
                <TimeTable
                  {...props}
                  value={curValue}
                  onChange={handleSelectChange}
                />
              </CalendarPart.Body>
              <CalendarPart.Footer
                right={
                  <>
                    <Button type="weak" htmlType="button" onClick={handleClose}>
                      {t.cancelText}
                    </Button>
                    <Button
                      type="primary"
                      htmlType="button"
                      onClick={handleOk}
                      disabled={!curValue}
                    >
                      {t.okText}
                    </Button>
                  </>
                }
              />
            </CalendarPart.Panel>
          </DropdownBox>
        }
      >
        <div
          ref={ref}
          className={classNames(`${classPrefix}-timepicker`, className)}
          style={style}
          {...mergeEventProps(props)}
        >
          <div
            className={classNames(`${classPrefix}-timepicker__input`, {
              "is-hover": hover && value && clearable,
            })}
            onMouseEnter={() => setHover(true)}
            onMouseLeave={() => setHover(false)}
          >
            <Input
              maxLength={showMilliseconds ? 12 : 8}
              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",
    RangePicker: TimeRangePicker,
  }
);

TimePicker.displayName = "TimePicker";
