import { ExpandedIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link, useLocation } from "react-router-dom";
import {
  useMenuState,
  MenuButton,
  MenuItem as BaseMenuItem,
} from "reakit/Menu";
import styled from "styled-components";
import { $Shape } from "utility-types";
import Flex from "~/components/Flex";
import MenuIconWrapper from "~/components/MenuIconWrapper";
import { actionToMenuItem } from "~/actions";
import useStores from "~/hooks/useStores";
import {
  Action,
  ActionContext,
  MenuSeparator,
  MenuHeading,
  MenuItem as TMenuItem,
} from "~/types";
import Header from "./Header";
import MenuItem, { MenuAnchor } from "./MenuItem";
import Separator from "./Separator";
import ContextMenu from ".";

type Props = {
  actions?: (Action | MenuSeparator | MenuHeading)[];
  context?: $Shape<ActionContext>;
  items?: TMenuItem[];
};

const Disclosure = styled(ExpandedIcon)`
  transform: rotate(270deg);
  position: absolute;
  right: 8px;
`;

const Submenu = React.forwardRef(
  (
    {
      templateItems,
      title,
      ...rest
    }: { templateItems: TMenuItem[]; title: React.ReactNode },
    ref: React.LegacyRef<HTMLButtonElement>
  ) => {
    const { t } = useTranslation();
    const menu = useMenuState({
      modal: true,
    });

    return (
      <>
        <MenuButton ref={ref} {...menu} {...rest}>
          {(props) => (
            <MenuAnchor {...props}>
              {title} <Disclosure color="currentColor" />
            </MenuAnchor>
          )}
        </MenuButton>
        <ContextMenu {...menu} aria-label={t("Submenu")}>
          <Template {...menu} items={templateItems} />
        </ContextMenu>
      </>
    );
  }
);

export function filterTemplateItems(items: TMenuItem[]): TMenuItem[] {
  let filtered = items.filter((item) => item.visible !== false);

  // this block literally just trims unnecessary separators
  filtered = filtered.reduce((acc, item, index) => {
    // trim separators from start / end
    if (item.type === "separator" && index === 0) return acc;
    if (item.type === "separator" && index === filtered.length - 1) return acc;

    // trim double separators looking ahead / behind
    const prev = filtered[index - 1];
    if (prev && prev.type === "separator" && item.type === "separator")
      return acc;

    // otherwise, continue
    return [...acc, item];
  }, []);

  return filtered;
}

function Template({ items, actions, context, ...menu }: Props) {
  const { t } = useTranslation();
  const location = useLocation();
  const stores = useStores();
  const { ui } = stores;
  const ctx = {
    t,
    isCommandBar: false,
    isContextMenu: true,
    activeCollectionId: ui.activeCollectionId,
    activeDocumentId: ui.activeDocumentId,
    location,
    stores,
    ...context,
  };

  const templateItems = actions
    ? actions.map((item) =>
        item.type === "separator" || item.type === "heading"
          ? item
          : actionToMenuItem(item, ctx)
      )
    : items || [];

  const filteredTemplates = filterTemplateItems(templateItems);

  const iconIsPresentInAnyMenuItem = filteredTemplates.find(
    (item) =>
      item.type !== "separator" && item.type !== "heading" && !!item.icon
  );

  return (
    <>
      {filteredTemplates.map((item, index) => {
        if (
          iconIsPresentInAnyMenuItem &&
          item.type !== "separator" &&
          item.type !== "heading"
        ) {
          item.icon = item.icon || <MenuIconWrapper />;
        }

        if (item.type === "route") {
          return (
            <MenuItem
              as={Link}
              to={item.to}
              key={index}
              disabled={item.disabled}
              selected={item.selected}
              icon={item.icon}
              {...menu}
            >
              {item.title}
            </MenuItem>
          );
        }

        if (item.type === "link") {
          return (
            <MenuItem
              href={item.href}
              key={index}
              disabled={item.disabled}
              selected={item.selected}
              level={item.level}
              target={item.href.startsWith("#") ? undefined : "_blank"}
              icon={item.icon}
              {...menu}
            >
              {item.title}
            </MenuItem>
          );
        }

        if (item.type === "button") {
          return (
            <MenuItem
              as="button"
              onClick={item.onClick}
              disabled={item.disabled}
              selected={item.selected}
              key={index}
              icon={item.icon}
              {...menu}
            >
              {item.title}
            </MenuItem>
          );
        }

        if (item.type === "submenu") {
          return (
            <BaseMenuItem
              key={index}
              as={Submenu}
              templateItems={item.items}
              title={<Title title={item.title} icon={item.icon} />}
              {...menu}
            />
          );
        }

        if (item.type === "separator") {
          return <Separator key={index} />;
        }

        if (item.type === "heading") {
          return <Header>{item.title}</Header>;
        }

        const _exhaustiveCheck: never = item;
        return _exhaustiveCheck;
      })}
    </>
  );
}

function Title({
  title,
  icon,
}: {
  title: React.ReactNode;
  icon?: React.ReactNode;
}) {
  return (
    <Flex align="center">
      {icon && <MenuIconWrapper>{icon}</MenuIconWrapper>}
      {title}
    </Flex>
  );
}

export default React.memo<Props>(Template);
