import { createStyles, Menu } from "@mantine/core";
import { BlockSchema } from "bocknoat-core";
import * as _ from "lodash";
import { useEffect, useRef } from "react";
import { ReactSlashMenuItem } from "../ReactSlashMenuItem";
import { SlashMenuItem } from "./SlashMenuItem";

export type SlashMenuProps<BSchema extends BlockSchema> = {
  items: ReactSlashMenuItem<BSchema>[];
  keyboardHoveredItemIndex: number;
  itemCallback: (item: ReactSlashMenuItem<BSchema>) => void;
};

export function SlashMenu<BSchema extends BlockSchema>(
  props: SlashMenuProps<BSchema>
) {
  const { classes } = createStyles({ root: {} })(undefined, {
    name: "SlashMenu",
  });
  const menuRef = useRef<HTMLDivElement>(null);
  const renderedItems: any[] = [];
  let index = 0;

  const groups = _.groupBy(props.items, (i) => i.group);

  _.forEach(groups, (el) => {
    renderedItems.push(
      <Menu.Label key={el[0].group}>{el[0].group}</Menu.Label>
    );

    for (const item of el) {
      renderedItems.push(
        <SlashMenuItem
          key={item.name}
          name={item.name}
          icon={item.icon}
          hint={item.hint}
          shortcut={item.shortcut}
          isSelected={props.keyboardHoveredItemIndex === index}
          set={() => props.itemCallback(item)}
        />
      );
      index++;
    }
  });

  useEffect(() => {
    setTimeout(() => {
      if (!menuRef.current) {
        return;
      }

      if (menuRef.current.childElementCount > 0) {
        // Reset any previously set max-height
        (menuRef.current.firstElementChild as HTMLDivElement).style.maxHeight =
          "none";

        // Get the menu DOM rect
        const domRect =
          menuRef.current.firstElementChild!.getBoundingClientRect();

        // Set the menu max height, based on the Tippy position. Checking if
        // the top of the menu is above the viewport (position < 0) is a quick
        // way to check if the placement is "top" or "bottom".
        (
          menuRef.current.firstElementChild as HTMLDivElement
        ).style.maxHeight = `${Math.min(
          domRect.top >= 0
            ? window.innerHeight - domRect.top - 20
            : domRect.bottom - 20
        )}px`;
      }
    }, 10);
  }, []);

  return (
    <div ref={menuRef}>
      <Menu
        /** Hacky fix to get the desired menu behaviour. The trigger="hover"
         * attribute allows focus to remain on the editor, allowing for suggestion
         * filtering. The closeDelay=10000000 attribute allows the menu to stay open
         * practically indefinitely, as normally hovering off it would cause it to
         * close due to trigger="hover".
         */
        defaultOpened={true}
        trigger={"hover"}
        closeDelay={10000000}>
        <Menu.Dropdown className={classes.root}>
          {renderedItems.length > 0 ? (
            renderedItems
          ) : (
            <Menu.Item>No match found</Menu.Item>
          )}
        </Menu.Dropdown>
      </Menu>
    </div>
  );
}
