import React, { cloneElement, isValidElement, useEffect, useImperativeHandle, useRef, useState } from "react";
import { createPopper, Placement } from "@popperjs/core";
import { usePopper } from 'react-popper';

import tw, { styled } from "twin.macro"


import { colors, colorsDisabled, colorsHover, textColors, textColorsHover } from "./Colors";

const DropDownItem = styled.button(({ type }) => [
  // The common button styles added with the tw import
  tw`text-sm text-left py-2 px-4 focus:outline-none block w-full whitespace-nowrap bg-transparent hover:bg-secondary disabled:bg-secondary text-primary hover:text-primary`,

  // Use props to conditionally style your components
  textColors[type], colorsHover[type]
])
export { DropDownItem }

let Dropdown = React.forwardRef(({ children, dropdownCss = null, onValueChanged = null, button, buttonProps = null, popper = null, placement = null, hover = null, ...props }: {
  children: React.ReactNode,
  dropdownCss?: any,
  onValueChanged?: (value: boolean) => void,
  button?: React.ReactNode,
  buttonProps?: any,
  popper?: any,
  placement?: Placement,
  hover?: boolean,
}, ref) => {
  // dropdown props
  const [overButton, setOverButton] = React.useState(false);
  const [overDropDown, setOverDropDown] = React.useState(false);

  const [dropdownPopoverShow, setDropdownPopoverShow] = React.useState(false);

  const [btnDropdownRef, setBtnDropdownRef] = React.useState(null);
  const [popoverDropdownRef, setPopoverDropdownRef] = React.useState(null);

  const { styles, attributes, update } = usePopper(btnDropdownRef, popoverDropdownRef,
    {
      placement: placement ?? "bottom",
      strategy: "fixed",
      ...popper
    });

  const openDropdownPopover = async () => {
    setDropdownPopoverShow(true);

    if (update != null) {
      await update();
    }

    try {
      if (onValueChanged)
        onValueChanged(true);
    } catch (e) {
      console.error(e);
    }
  };
  const closeDropdownPopover = () => {
    setDropdownPopoverShow(false);
    try {
      if (onValueChanged)
        onValueChanged(false);
    } catch (e) {
      console.error(e);
    }
  };

  function handleOutsideClick(event) {
    let node = popoverDropdownRef;
    if (node && !node?.contains(event.target) && !btnDropdownRef?.contains(event.target)) {
      closeDropdownPopover();
    }
  }

  useEffect(() => {
    if ("ontouchend" in window) {
      document.addEventListener("touchend", handleOutsideClick)
    } else {
      document.addEventListener("click", handleOutsideClick)
    }
    // Specify how to clean up after this effect:
    return function cleanup() {
      document.removeEventListener("touchend", handleOutsideClick)
      document.removeEventListener("click", handleOutsideClick)
    };
  });
  /*
    useImperativeHandle(ref, () => ({
      openDropdownPopover,
      closeDropdownPopover
    }))*/

  let childrenWithProps = React.Children.map(children, (item) => {
    if (!item)
      return;
    //check that item is not only ReactNode
    if (!isValidElement(item))
      return;
    let click = item.props?.onClick;
    let close = item.props?.closeOnClick ?? true;

    return cloneElement(item as any, { key: Array.isArray(children) ? children.indexOf(item) : 0, onClick: () => { close && closeDropdownPopover(); click && click() } });
  });
  return (
    <>
      <button
        ref={setBtnDropdownRef}
        onClick={async (e) => {
          e.preventDefault();
          dropdownPopoverShow ? closeDropdownPopover() : await openDropdownPopover();
        }}
        onMouseEnter={async () => { setOverButton(true); hover && await openDropdownPopover() }}
        onMouseLeave={() => { setOverButton(false); (!overDropDown && hover) && closeDropdownPopover() }}
        {...buttonProps}
      >
        {button}
      </button>

      {dropdownPopoverShow && <div
        {...props}
        ref={setPopoverDropdownRef}
        onMouseEnter={async () => { setOverDropDown(true); hover && await openDropdownPopover() }}
        onMouseLeave={() => { setOverDropDown(false); (!overButton && hover) && closeDropdownPopover() }}
        style={styles.popper}
        {...attributes.popper}
        css={[
          dropdownPopoverShow ? tw`block` : tw`hidden`,
          tw`bg-trinary text-base z-50 float-left rounded-xl overflow-hidden text-left shadow-lg`,
          dropdownCss,
          (props as any).css
        ]}
      >
        {childrenWithProps}
      </div>}

    </>
  );
});
export default Dropdown;

export function DropdownMenu({ children, fallback, button, notSelectedOne = true, value, onChange, ...props }) {
  let ref = useRef()

  let btn = fallback ?? "";
  if (children && children.length > 0)
    btn = children[value > children.length ? 0 : value]

  if (button != null) {
    btn = button(btn, value > children.length ? 0 : value);
  }

  return <>
    <Dropdown ref={ref} button={btn} {...props}>
      {
        children && children.map((i) => {
          if (children.indexOf(i) == value)
            return;
          return <DropDownItem key={children.indexOf(i)} onClick={() => onChange(children.indexOf(i))}>{i}</DropDownItem>
        })
      }
      {
        (children == null || children.length <= (notSelectedOne ? 1 : 0)) && <p tw="p-4 text-secondary">Nothing to show</p>
      }
    </Dropdown>
  </>
}