import React, { useRef, useImperativeHandle } from "react";
import classNames from "classnames";
import { InputProps, Input } from "../input";
import { Button } from "../button";
import { useDefaultValue, ChangeContext } from "../form/controlled";
import { ScaleTransition } from "../transition";
import { useConfig } from "../_util/config-context";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { noop } from "../_util/noop";
import { KeyMap } from "../_util/key-map";
import { callBoth } from "../_util/call-both";
import { mergeRefs } from "../util";

export interface SearchBoxProps extends InputProps {
  /**
   * 是否为多行搜索模式
   *
   * @default false
   */
  multiline?: boolean;

  /**
   * 是否为简洁模式
   *
   * @default false
   */
  simple?: boolean;

  /**
   * 点击搜索或输入回车时回调
   * 如果自行绑定了 `onKeydown` 处理方法，则该回调不再提供
   */
  onSearch?: (keyword?: string, context?: ChangeContext) => void;

  /**
   * 用户清空搜索时回调
   * */
  onClear?: () => void;

  /**
   * 搜索框尺寸
   */
  size?: "full" | "l" | "m" | "s";

  /**
   * 显示帮助按钮
   *
   * @default false
   * @since 2.2.2
   */
  showHelp?: boolean;

  /**
   * 帮助按钮点击回调
   *
   * @since 2.2.2
   */
  onHelp?: (e: React.MouseEvent) => void;

  /**
   * 搜索框容器 ref
   *
   * *组件 `ref` 指向了内部输入框*
   *
   * @since 2.7.0
   */
  searchBoxRef?: React.Ref<HTMLDivElement>;
}

export const SearchBox = forwardRefWithStatics(
  function SearchBox(props: SearchBoxProps, ref: React.Ref<HTMLInputElement>) {
    const { classPrefix } = useConfig();
    const {
      multiline,
      simple,
      onSearch = noop,
      onClear = noop,
      className,
      style,
      size,
      showHelp,
      disabled,
      onHelp = noop,
      searchBoxRef,
      ..._inputProps
    } = props;

    const inputBoxClassNames = classNames({
      [`${classPrefix}-search`]: true,
      [`${classPrefix}-search--multi`]: multiline,
      [`${classPrefix}-search--simple`]: simple,
      [className]: className,
    });

    const inputProps = useDefaultValue(_inputProps, "");

    const composingRef = useRef<boolean>(null);

    // 需要使用到 inputRef，同时向外部暴露
    const inputRef = useRef<HTMLInputElement>(null);
    useImperativeHandle(ref, () => inputRef.current);

    return (
      <div
        className={classNames(`${classPrefix}-form--search`, {
          [`${classPrefix}-search--has-info`]: showHelp,
          [`size-${size === "full" ? `${size}-width` : size}`]: size,
        })}
        ref={mergeRefs(searchBoxRef)}
      >
        <div className={inputBoxClassNames} style={style}>
          <div className={`${classPrefix}-search__inner`}>
            <Input
              type="search"
              disabled={disabled}
              multiline={multiline}
              baseClassName={`${classPrefix}-input`}
              className={`${classPrefix}-input--search`}
              onKeyDown={event => {
                const { key, ctrlKey, metaKey } = event;
                if (key === KeyMap.Esc) {
                  event.preventDefault();
                  inputProps.onChange("", { event });
                  onClear();
                }
                // 回车处理 onSearch 逻辑
                if (key !== KeyMap.Enter) {
                  return;
                }
                // 多行模式下，要按下 ctrl 或者 command
                if (multiline && !metaKey && !ctrlKey) {
                  return;
                }
                event.preventDefault();
                if (!composingRef.current) {
                  onSearch(event.currentTarget.value, { event });
                }
              }}
              {...inputProps}
              onCompositionStart={callBoth(() => {
                composingRef.current = true;
              }, inputProps.onCompositionStart)}
              onCompositionEnd={callBoth(() => {
                composingRef.current = false;
              }, inputProps.onCompositionEnd)}
              ref={inputRef}
            />
          </div>
          <ScaleTransition in={/\S/.test(inputProps.value)}>
            <Button
              icon="dismiss"
              htmlType="button"
              disabled={disabled}
              className={`${classPrefix}-btn--dismiss`}
              onClick={event => {
                event.preventDefault();
                inputProps.onChange("", { event });
                onClear();
                if (inputRef.current) {
                  inputRef.current.focus();
                }
              }}
            />
          </ScaleTransition>
          {showHelp && (
            <Button
              icon="info"
              htmlType="button"
              disabled={disabled}
              className={`${classPrefix}-btn--info`}
              onClick={onHelp}
            />
          )}
          <Button
            icon="search"
            htmlType="button"
            disabled={disabled}
            className={`${classPrefix}-btn--search`}
            onClick={event => {
              event.preventDefault();
              onSearch(inputProps.value, { event });
            }}
          />
        </div>
      </div>
    );
  },
  {
    defaultLabelAlign: "middle",
  }
);

SearchBox.displayName = "SearchBox";
