import React, { CSSProperties, ReactNode, useMemo, useState } from "react";
import classNames from "classnames";
import { Modifier } from "react-popper-2";
import { MenuListComponentProps } from "react-select";
import FocusLock from "react-focus-lock";
import { tokens } from "../../tokens";
import { useOutsideClick } from "../../hooks";
import { Box } from "../Box";
import { Flex } from "../Flex";
import { Stack } from "../Stack";
import { PopoverMenu } from "../PopoverMenu";
import { Icon, ICON_TYPE } from "../Icon";
import { CloseButton } from "../CloseButton";
import { Text } from "../Text";
import { Select, SelectProps } from "./Select";
import { Popover } from "../Popover";
import { PopperPlacement } from "../../types";

interface SelectPopoverProps {
  readView: ReactNode;
  cancelButton: ReactNode;
  onCancelEdit: () => void;
  selectProps: SelectProps;
  preventOutsideClick?: boolean;
  optionsMessage?: string;
  isCreatable?: boolean;
  className?: string;
  style?: CSSProperties;
}

export const SelectPopover = ({
  readView,
  cancelButton,
  onCancelEdit,
  preventOutsideClick = false,
  optionsMessage,
  isCreatable,
  selectProps,
  className,
  style,
}: SelectPopoverProps) => {
  const containerElement = React.useRef<HTMLDivElement>(null);
  const selectWrapperElement = React.useRef<HTMLDivElement>(null);
  const readViewWrapperElement = React.useRef<HTMLDivElement>(null);

  useOutsideClick(
    [containerElement, selectWrapperElement, readViewWrapperElement],
    () => {
      if (!preventOutsideClick) {
        onCancelEdit();
      }
    },
  );

  const selectStyles = {
    control: (provided: any) => ({
      ...provided,
      width: `calc(100% - ${tokens.margin[6]})`,
      margin: tokens.margin[3],
    }),
    menu: (provided: any) => ({
      ...provided,
      boxShadow: "none !important", // override Arrow classname
      borderRadius: "0 !important", // override Arrow classname
      zIndex: "1 !important", // override Arrow classname
      backgroundColor: tokens.colors.white,
      position: "relative",
      marginTop: tokens.margin[3],
      marginBottom: 0,
      borderTop: `1px solid ${tokens.colors.gray[300]}`,
      maxHeight: "184px",
      overflowY: "auto",
    }),
  };

  const components = {
    DropdownIndicator: () => <Icon icon={ICON_TYPE.SEARCH} />,
    MenuList: (props: MenuListComponentProps<any>) => {
      const { children, innerRef, options } = props;
      return (
        <Box ref={innerRef}>
          <Box>{children}</Box>
          {options.length > 0 && optionsMessage && (
            <Text className="text-gray-600 text-icon leading-normal px-3 py-2">
              {optionsMessage}
            </Text>
          )}
        </Box>
      );
    },
    IndicatorSeparator: null,
  };

  const sharedProps = {
    isCreatable,
    onBlur: () => {}, // prevent onBlur from closing popover; rely on useOutsideClick
    components,
    styles: selectStyles,
    tabSelectsValue: false,
    closeMenuOnSelect: false,
    menuInPortal: false,
    backspaceRemovesValue: false,
    controlShouldRenderValue: false,
    hideSelectedOptions: false,
    isClearable: false,
    menuIsOpen: true,
    autoFocus: true,
    className: classNames("w-full"),
  };

  const initialPlacement = "bottom";
  const [currentPlacement, setCurrentPlacement] = useState<PopperPlacement>(
    initialPlacement,
  );
  const popperModifiers: Modifier<any, any>[] = useMemo(
    () => [
      {
        name: "sameWidth",
        enabled: true,
        phase: "beforeWrite",
        requires: ["computeStyles"],
        fn({ state }) {
          setCurrentPlacement(state.placement);
          const newState = state;
          newState.styles.popper.width = `${state.rects.reference.width}px`;
          return newState;
        },
      },
      {
        name: "offset",
        options: {
          offset: [0, -1],
        },
      },
    ],
    [],
  );

  return (
    <FocusLock>
      <Popover
        isVisible
        className={classNames(
          "rounded-tl-none",
          "rounded-tr-none",
          currentPlacement === "top" ? "shadow-up" : "shadow",
          "box-content",
          "border",
          "border-gray-300",
        )}
        popperModifiers={popperModifiers}
        removePadding
        placement={initialPlacement}
        referenceElement={
          <PopoverMenu
            ref={containerElement}
            className={classNames(
              "w-full absolute",
              "shadow",
              currentPlacement === "bottom"
                ? "rounded-bl-none rounded-br-none"
                : "rounded-tl-none rounded-tr-none",
              className,
            )}
            style={style}
          >
            <Flex
              className="px-2 py-1 items-center w-full border-b border-gray-300"
              ref={readViewWrapperElement}
            >
              {readView}

              <Stack spacing={1} className="ml-auto flex-shrink-0 self-start">
                <CloseButton
                  className="text-iconSm"
                  onClick={() => {
                    if (selectProps.onChange) {
                      if (selectProps.isMulti) {
                        selectProps.onChange([], { action: "clear" });
                      } else {
                        selectProps.onChange(
                          { label: "", value: "" },
                          { action: "clear" },
                        );
                      }
                    }
                  }}
                />
                {cancelButton}
              </Stack>
            </Flex>
          </PopoverMenu>
        }
      >
        <Box ref={selectWrapperElement}>
          {isCreatable ? (
            <Select.Creatable {...selectProps} {...sharedProps} />
          ) : (
            <Select {...selectProps} {...sharedProps} />
          )}
        </Box>
      </Popover>
    </FocusLock>
  );
};
