import * as React from "react"

import { cn } from "@/lib/utils"

import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"

import { Command, MenuListProps } from "./types"
import { Icon } from "../../components/ui/icon"
import { PopoverWrapper } from "../../components/ui/popover-wrapper"
import { Shortcut } from "../../components/ui/shortcut"
import { getShortcutKeys } from "@shared/utils"

export const MenuList = React.forwardRef((props: MenuListProps, ref) => {
  const scrollContainer = React.useRef<HTMLDivElement>(null)
  const activeItem = React.useRef<HTMLButtonElement>(null)
  const [selectedGroupIndex, setSelectedGroupIndex] = React.useState(0)
  const [selectedCommandIndex, setSelectedCommandIndex] = React.useState(0)

  // Anytime the groups change, i.e. the user types to narrow it down, we want to
  // reset the current selection to the first menu item
  React.useEffect(() => {
    setSelectedGroupIndex(0)
    setSelectedCommandIndex(0)
  }, [props.items])

  const selectItem = React.useCallback(
    (groupIndex: number, commandIndex: number) => {
      const command = props.items[groupIndex].commands[commandIndex]
      props.command(command)
    },
    [props],
  )

  React.useImperativeHandle(ref, () => ({
    onKeyDown: ({ event }: { event: React.KeyboardEvent }) => {
      if (event.key === "ArrowDown") {
        if (!props.items.length) {
          return false
        }

        const commands = props.items[selectedGroupIndex].commands

        let newCommandIndex = selectedCommandIndex + 1
        let newGroupIndex = selectedGroupIndex

        if (commands.length - 1 < newCommandIndex) {
          newCommandIndex = 0
          newGroupIndex = selectedGroupIndex + 1
        }

        if (props.items.length - 1 < newGroupIndex) {
          newGroupIndex = 0
        }

        setSelectedCommandIndex(newCommandIndex)
        setSelectedGroupIndex(newGroupIndex)

        return true
      }

      if (event.key === "ArrowUp") {
        if (!props.items.length) {
          return false
        }

        let newCommandIndex = selectedCommandIndex - 1
        let newGroupIndex = selectedGroupIndex

        if (newCommandIndex < 0) {
          newGroupIndex = selectedGroupIndex - 1
          newCommandIndex = props.items[newGroupIndex]?.commands.length - 1 || 0
        }

        if (newGroupIndex < 0) {
          newGroupIndex = props.items.length - 1
          newCommandIndex = props.items[newGroupIndex].commands.length - 1
        }

        setSelectedCommandIndex(newCommandIndex)
        setSelectedGroupIndex(newGroupIndex)

        return true
      }

      if (event.key === "Enter") {
        if (
          !props.items.length ||
          selectedGroupIndex === -1 ||
          selectedCommandIndex === -1
        ) {
          return false
        }

        selectItem(selectedGroupIndex, selectedCommandIndex)

        return true
      }

      return false
    },
  }))

  React.useEffect(() => {
    if (activeItem.current && scrollContainer.current) {
      const offsetTop = activeItem.current.offsetTop
      const offsetHeight = activeItem.current.offsetHeight

      scrollContainer.current.scrollTop = offsetTop - offsetHeight
    }
  }, [selectedCommandIndex, selectedGroupIndex])

  const createCommandClickHandler = React.useCallback(
    (groupIndex: number, commandIndex: number) => {
      return () => {
        selectItem(groupIndex, commandIndex)
      }
    },
    [selectItem],
  )

  if (!props.items.length) {
    return null
  }

  return (
    <PopoverWrapper
      ref={scrollContainer}
      className="flex max-h-[min(80vh,24rem)] flex-col overflow-auto p-1"
    >
      {props.items.map((group, groupIndex: number) => (
        <React.Fragment key={group.title}>
          {group.commands.map((command: Command, commandIndex: number) => (
            <Button
              key={command.label}
              variant="ghost"
              onClick={createCommandClickHandler(groupIndex, commandIndex)}
              className={cn(
                "relative w-full justify-between gap-2 px-3.5 py-1.5 font-normal",
                {
                  "bg-accent text-accent-foreground":
                    selectedGroupIndex === groupIndex &&
                    selectedCommandIndex === commandIndex,
                },
              )}
            >
              <Icon name={command.iconName} />
              <span className="truncate text-sm">{command.label}</span>
              <div className="flex flex-auto flex-row"></div>
              <Shortcut.Wrapper
                ariaLabel={getShortcutKeys(command.shortcuts)
                  .map((shortcut) => shortcut.readable)
                  .join(" + ")}
              >
                {command.shortcuts.map((shortcut) => (
                  <Shortcut.Key shortcut={shortcut} key={shortcut} />
                ))}
              </Shortcut.Wrapper>
            </Button>
          ))}
          {groupIndex !== props.items.length - 1 && (
            <Separator className="my-1.5" />
          )}
        </React.Fragment>
      ))}
    </PopoverWrapper>
  )
})

MenuList.displayName = "MenuList"

export default MenuList
