import React, { Fragment, useState } from "react";
import classNames from "classnames";
import { Popover } from "../popover";
import { Input } from "../input";
import { Icon } from "../icon";
import { Text } from "../text";
import {
  CascaderData,
  CascaderOption,
  CascaderSingleProps,
} from "./CascaderProps";
import { CascaderBox, getOptions } from "./CascaderBox";
import { CascaderMenuBox } from "./CascadeMenu";
import { CascaderSearch } from "./CascaderSearch";
import { useDefaultValue } from "../form";
import { useTranslation } from "../i18n";
import { useDefault } from "../_util/use-default";
import { useConfig } from "../_util/config-context";
import { noop } from "../_util/noop";
import { mergeEventProps } from "../_util/merge-event-props";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";

const defaultValueRender = (options: CascaderOption[]) =>
  options.map(({ label, value }, index) => (
    <Fragment key={`${value}-${index}`}>
      {index !== 0 && " / "}
      {label || value}
    </Fragment>
  ));

export const CascaderSingle = forwardRefWithStatics(
  function CascaderSingle(
    props: CascaderSingleProps & { data: CascaderData },
    ref: React.Ref<HTMLDivElement>
  ) {
    const t = useTranslation();
    const { classPrefix } = useConfig();
    const {
      data,
      value,
      onChange,
      onLoad,
      disabled,
      changeOnSelect,
      style,
      className,
      placeholder = t.pleaseSelect,
      defaultOpen = false,
      open,
      onOpenChange = noop,
      placement = "bottom-start",
      placementOffset = 5,
      closeOnScroll = true,
      escapeWithReference,
      popupContainer,
      overlayClassName,
      overlayStyle,
      clearable,
      searchable,
      filter,
      type,
      expandTrigger,
      valueRender = defaultValueRender,
      tips,
      bottomTips,
      onScrollBottom = noop,
      onSearch = noop,
    } = useDefaultValue(props, []);
    const [hover, setHover] = useState<boolean>(false);
    const [isOpened, setIsOpened] = useDefault(open, defaultOpen, onOpenChange);

    const [focused, setFocused] = useState<boolean>(false);
    const [searching, setSearching] = useState<boolean>(false);
    const [searchValue, setSearchValue] = useState<string>("");

    const options = getOptions(data, value);

    const Box = type === "menu" ? CascaderMenuBox : CascaderBox;

    return (
      <Popover
        trigger={disabled ? "empty" : "click"}
        visible={isOpened}
        onVisibleChange={setIsOpened}
        onExited={() => {
          setSearchValue("");
          setSearching(false);
        }}
        placement={placement || "bottom-start"}
        placementOffset={placementOffset}
        closeOnScroll={closeOnScroll}
        escapeWithReference={escapeWithReference}
        popupContainer={popupContainer}
        overlayClassName={overlayClassName}
        overlayStyle={overlayStyle}
        overlay={({ scheduleUpdate }) => {
          if (searchValue) {
            return (
              <CascaderSearch
                data={data}
                searchValue={searchValue}
                filter={filter}
                onChange={(...args) => {
                  onChange(...args);
                  setIsOpened(false);
                  setSearching(false);
                }}
                scheduleUpdate={scheduleUpdate}
                changeOnSelect={changeOnSelect}
                tips={tips}
                bottomTips={bottomTips}
                onScrollBottom={onScrollBottom}
                onSearch={onSearch}
              />
            );
          }
          return (
            <Box
              data={data}
              curValue={value}
              onLoad={onLoad}
              onSelect={onChange}
              onClose={() => setIsOpened(false)}
              changeOnSelect={changeOnSelect}
              scheduleUpdate={scheduleUpdate}
              expandTrigger={expandTrigger}
              tips={tips}
              bottomTips={bottomTips}
              onScrollBottom={onScrollBottom}
            />
          );
        }}
      >
        <div
          ref={ref}
          className={classNames(`${classPrefix}-cascader`, className, {
            [`${classPrefix}-cascader--search`]: searchable,
            "is-disabled": disabled,
          })}
          style={style}
          {...mergeEventProps(props, {
            onMouseEnter: () => setHover(true),
            onMouseLeave: () => setHover(false),
          })}
        >
          <div className={`${classPrefix}-cascader__input`}>
            {!searching && (
              <div className={`${classPrefix}-cascader__value`}>
                <Text theme={focused ? "weak" : undefined}>
                  {valueRender(options)}
                </Text>
              </div>
            )}
            <Input
              readonly={!searchable}
              autoComplete="off"
              disabled={disabled}
              placeholder={value.length ? "" : placeholder}
              value={searching ? searchValue : ""}
              onFocus={() => {
                if (searchable) {
                  setFocused(true);
                  setIsOpened(true);
                  if (searchValue) {
                    setSearching(true);
                  }
                }
              }}
              onBlur={() => {
                setFocused(false);
                setSearching(false);
              }}
              onChange={value => {
                setSearchValue(value);
              }}
              onInput={event => {
                setSearching(!!event.currentTarget.value);
              }}
              onClick={event => searchable && event.stopPropagation()}
            />
          </div>
          {clearable && !disabled && hover && value.length ? (
            <Icon
              type="dismiss"
              onClick={event => {
                event.stopPropagation();
                onChange([], { event, options: [] });
                setIsOpened(false);
              }}
            />
          ) : (
            <Icon type="arrowdown" />
          )}
        </div>
      </Popover>
    );
  },
  {
    defaultLabelAlign: "middle",
  }
);

CascaderSingle.displayName = "CascaderSingle";
