/* global document */
import React, {
  useCallback,
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
} from "react";
import { findNodeHandle, ViewStyle } from "react-native";

import theme from "mazlo-theme";

import useScroll from "../../hooks/useScroll";
import { Button } from "../Button";
import { Icon } from "../Icon";
import { Portal as MenuPortal } from "../Portal";
import { Text } from "../Text";
import { View } from "../View";
import styles from "./styles";

type Offset = {
  x?: number;
  y?: number;
};

export type Props = {
  alignHorizontal?: "left" | "right";
  alignVertical?: "top" | "bottom";
  headerComponent?: React.ReactNode;
  menuStyle?: ViewStyle;
  offset?: Offset;
  open?: boolean;
  dark?: boolean;
  decorated?: boolean;
  children: React.ReactNode;
  label: string;
  toggleIconComponent?: React.ReactNode;
  variant?: "blank" | "content";
};

const Menu = ({
  alignHorizontal = "left",
  alignVertical = "bottom",
  headerComponent,
  menuStyle,
  open,
  offset,
  dark,
  decorated,
  children,
  label,
  toggleIconComponent,
  variant = "blank",
}: Props) => {
  const node = useRef();
  const menuNode = useRef();
  const { x: offsetLeft = 0, y: offsetTop = 0 } = offset || {};

  const [showOptions, setShowOptions] = useState(false);
  const menuOpen = open == null ? showOptions : open;

  const [layout, setLayout] = useState({ width: 0, height: 0, x: 0, y: 0 });
  const { scrollX, scrollY } = useScroll();
  useLayoutEffect(() => {
    const nodeHandle = findNodeHandle(node.current) as any;
    if (nodeHandle) {
      setLayout(nodeHandle.getBoundingClientRect().toJSON());
    }
  }, [menuOpen, node.current, scrollX, scrollY]);

  const handleMouseDown = useCallback(
    (e) => {
      const currentNode = findNodeHandle(node.current) as any;
      const currentMenu = findNodeHandle(menuNode.current) as any;
      const portal = document.querySelector("#portal");
      if (
        currentNode &&
        !currentNode.contains(e.target) &&
        currentMenu &&
        !currentMenu.contains(e.target) &&
        portal &&
        !portal.contains(e.target)
      ) {
        setShowOptions(false);
      }
    },
    [node.current, menuNode.current]
  );

  useEffect(() => {
    if (open == null) {
      if (menuOpen) {
        document.addEventListener("mousedown", handleMouseDown);
      } else {
        document.removeEventListener("mousedown", handleMouseDown);
      }
      return () => {
        document.removeEventListener("mousedown", handleMouseDown);
      };
    }
  }, [open, menuOpen]);

  return (
    <View style={styles.container} ref={node}>
      <Button
        onPress={() => setShowOptions((prevShowOptions) => !prevShowOptions)}
      >
        <Text variant={dark ? "white" : undefined}>{label}</Text>
        <View style={styles.toggleIconContainer}>
          {toggleIconComponent ? (
            toggleIconComponent
          ) : (
            <Icon
              color={dark ? theme.colors.white : theme.colors.dark}
              name="small-arrow-down"
              style={styles.arrowIcon}
            />
          )}
        </View>
      </Button>
      {menuOpen && decorated && (
        <View style={styles.toggleContainerDecorator} />
      )}
      <MenuPortal>
        <View
          ref={menuNode}
          style={[
            styles.menuOrigin,
            {
              width: layout.width,
              top:
                layout.y +
                offsetTop +
                scrollY +
                (alignVertical === "bottom" ? layout.height : 0),
              left: layout.x + scrollX + offsetLeft,
            },
          ]}
        >
          {menuOpen && (
            <View
              style={[
                styles.menuContainer,
                decorated && styles.menuContainerDecorated,
                alignHorizontal === "right" && styles.menuContainerRight,
                alignVertical === "top" && styles.menuContainerTop,
                styles[`${variant}Container`],
                menuStyle,
              ]}
            >
              {headerComponent}
              <View style={styles.menuBody}>{children}</View>
            </View>
          )}
        </View>
      </MenuPortal>
    </View>
  );
};

export default Menu;
