import React, {
  useState,
  useContext,
  useEffect,
  useCallback,
  useRef,
} from "react";
import Flex from "./Flex";
import ReactDOM from "react-dom";
import styled, { css } from "styled-components/macro";

const mql = window.matchMedia(`(min-width: 800px)`);

type TReactDispatch = React.DispatchWithoutAction;
interface IDropdownContext {
  toggle: TReactDispatch;
  close: TReactDispatch;
  isDropdownOpen: boolean;
}

const DropdownContext = React.createContext<IDropdownContext>({
  toggle: () => {},
  close: () => {},
  isDropdownOpen: false,
});

interface DropdownProps {
  onChange?: (toggle: boolean) => void;
  shouldCloseOnClick?: boolean;
}

type ToggleFuncProps = (toggle: TReactDispatch, close: TReactDispatch) => {};

// https://stackoverflow.com/a/51835761/10629172
interface DropdownToggleProps extends React.HTMLAttributes<HTMLDivElement> {}
interface DropdownContentProps extends React.HTMLAttributes<HTMLDivElement> {}

type StaticComponents = {
  Toggle: React.FC<DropdownToggleProps>;
  Content: React.FC<DropdownContentProps>;
  Item: React.FC;
};

export const Dropdown: React.FC<DropdownProps> & StaticComponents = ({
  children,
  onChange,
  shouldCloseOnClick,
  ...props
}) => {
  const id = useRef<HTMLDivElement | null>(null);
  const [isDropdownOpen, setDropdown] = useState<boolean>(false);
  const toggle = () => {
    setDropdown(!isDropdownOpen);
  };
  const close = () => {
    setDropdown(false);
  };

  const closeDropdown = useCallback(
    e => {
      // check click inside dropdown content
      if (e.target.closest(".dropdown--content") && shouldCloseOnClick) {
        close();
        return;
      }
      // close all other dropdowns
      if (e.target.closest(".dropdown") !== id.current) {
        close();
        return;
      }
    },
    [shouldCloseOnClick]
  );

  useEffect(() => {
    onChange && onChange(isDropdownOpen);
  }, [isDropdownOpen, onChange]);

  useEffect(() => {
    window.addEventListener("click", closeDropdown);
    return () => window.removeEventListener("click", closeDropdown);
  }, [closeDropdown]);

  const value = React.useMemo(() => ({ isDropdownOpen, toggle, close }), [
    isDropdownOpen,
    toggle,
    close,
  ]);

  return (
    <div className="dropdown__container" {...props}>
      <DropdownContext.Provider value={value}>
        <div ref={id} className={`dropdown`} style={{ position: "relative" }}>
          {children}
        </div>
      </DropdownContext.Provider>
    </div>
  );
};

const Toggle: React.FC<DropdownToggleProps> = ({ children }) => {
  const { toggle, close } = useContext(DropdownContext);

  return (
    <div data-testid="dropdown-toggle">
      {React.isValidElement(children)
        ? React.cloneElement(children, { onClick: toggle })
        : (children as ToggleFuncProps)(toggle, close)}
    </div>
  );
};
Dropdown.Toggle = React.memo(Toggle);

const Content: React.FC<DropdownContentProps> = ({ children, ...props }) => {
  const { isDropdownOpen } = useContext(DropdownContext);
  const isMobile = !mql.matches;

  // Backdrop in mobile
  useEffect(() => {
    if (isMobile && isDropdownOpen) {
      document.body.classList.add("bodyOverlayBlur");
    }
    return () => document.body.classList.remove("bodyOverlayBlur");
  }, [isDropdownOpen]);

  const RenderContent = (
    <StyledDropdownContent
      data-testid="dropdown-content"
      className="dropdown--content"
      isOpen={isDropdownOpen}
      {...props}
    >
      <Flex direction="column">{isDropdownOpen && children}</Flex>
    </StyledDropdownContent>
  );

  return isMobile
    ? ReactDOM.createPortal(RenderContent, document.body)
    : RenderContent;
};
Dropdown.Content = React.memo(Content);

const Item: React.FC = ({ children }) => {
  return <div className="dropdown__item">{children}</div>;
};
Dropdown.Item = Item;

const StyledDropdownContent = styled.div<{ isOpen?: boolean }>`
  width: max-content;
  padding: 15px;
  border-radius: 5px;
  position: absolute;
  right: 0;
  margin-top: 5px;
  background-color: ${p => p.theme.colors.dark1};
  opacity: 0;
  pointer-events: none;
  transform: translateY(-25px);
  transition: 0.2s;
  z-index: 1;
  ${p =>
    p.isOpen &&
    css`
      opacity: 1;
      pointer-events: all;
      transform: translateY(5px);
    `}

  .dropdown__item {
    width: 100%;
    button {
      width: 100%;
    }
  }
  @media screen and (max-width: 400px) {
    .dropdown__item {
      button {
        padding: 15px 10px;
      }
    }
  }

  @media (${p => p.theme.media.tablet}) {
    position: fixed;
    left: 0;
    top: var(--app-height);
    width: 100vw;
    height: auto;
    margin: 0;
    padding: 20px;
    border-radius: 0;
    transform: ${p => (p.isOpen ? "translateY(-100%)" : "translateY(0%)")};
  }
`;