import React, { ReactNode, useCallback } from "react";
import classnames from "classnames";
import PropTypes from "prop-types";
import NavbarCollapse from "./navbar-collapse";
import { GRID_BREAKPOINTS } from "../utils/grid-breakpoints";
import { getPressedKey, KEYS } from "../utils/keyboard";

interface INavbarDropdownProps {
  children?: ReactNode | ReactNode[];
  id: string;
  className?: string;
  title: string | ReactNode;
  position?: "right" | "left";
  arrow?: boolean;
  hasDropdownArrow?: boolean;
  expand?: "sm" | "md" | "lg" | "xl";
}

const NavbarDropdown = (props: INavbarDropdownProps) => {
  const {
    id,
    className,
    children,
    position = "left",
    arrow = true,
    hasDropdownArrow = false,
    title,
    expand = "md"
  } = props;

  // we unfortunately need this to avoid a bug, these are the same breakpoints used by bootstrap/honeyUI and they shuld never change
  const navGridBreakPoint = GRID_BREAKPOINTS[expand];

  const [isMenuExpanded, setMenuExpanded] = React.useState(false);
  const autoSetMenuExpanded = () => setMenuExpanded(!isMenuExpanded);

  const [isMenuHovered, setMenuHovered] = React.useState(false);
  const [isMenuHoveredAnimate, setMenuHoveredAnimate] = React.useState(false);
  const autoSetMenuHovered = (show: boolean) => {
    if (show) {
      setTimeout(() => {
        setMenuHoveredAnimate(true);
      });
      setMenuHovered(true);
    } else {
      setTimeout(() => {
        setMenuHovered(false);

        // check if we are on mobile, without this check, the mobile menu would act as an accordion,
        // also this check is needed for desktop devices so menus properly dismiss on blur event
        if (window.innerWidth >= navGridBreakPoint) {
          setMenuExpanded(false);
        }
      }, 150);
      setMenuHoveredAnimate(false);
    }
  };

  const onKeyPress = useCallback(
    (e: React.KeyboardEvent) => {
      const key = getPressedKey(e);
      if (key === KEYS.Enter) {
        autoSetMenuExpanded();
      }
    },
    [autoSetMenuExpanded]
  );

  return (
    <li
      className={classnames("nav-item dropdown", className)}
      onMouseEnter={() => {
        autoSetMenuHovered(true);
      }}
      onMouseLeave={() => {
        autoSetMenuHovered(false);
      }}
    >
      <div
        className={classnames(
          "dropdown-toggle",
          hasDropdownArrow ? `p-2 px-${expand}-3` : "nav-link",
          { active: isMenuExpanded }
        )}
        id={id}
        data-testid={id}
        data-toggle="dropdown"
        aria-haspopup="true"
        role="presentation"
        onClick={autoSetMenuExpanded}
        onKeyPress={onKeyPress}
      >
        {title}
      </div>
      <NavbarCollapse className="dropdown-submenu" isOpen={isMenuExpanded}>
        {arrow && (
          <div
            className={classnames(
              "dropdown-arrow",
              { show: isMenuExpanded || isMenuHovered },
              { hover: isMenuHoveredAnimate }
            )}
          ></div>
        )}
        <div
          role="presentation"
          className={classnames(
            "dropdown-menu",
            { "dropdown-menu-right": position === "right" },
            { show: isMenuExpanded || isMenuHovered },
            { hover: isMenuHoveredAnimate }
          )}
          aria-labelledby={id}
          data-testid={`${id}-dropdown-body`}
        >
          {children}
        </div>
      </NavbarCollapse>
    </li>
  );
};

NavbarDropdown.displayName = "NavbarDropdown";
NavbarDropdown.defaultProps = {
  position: "left"
};
NavbarDropdown.propTypes = {
  children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.element), PropTypes.element]),
  id: PropTypes.string.isRequired,
  className: PropTypes.string,
  title: PropTypes.string.isRequired,
  position: PropTypes.oneOf(["right", "left"]),
  arrow: PropTypes.bool,
  expand: PropTypes.oneOf(["sm", "md", "lg", "xl"])
};

export default NavbarDropdown;
