import { Extension, ReactRenderer } from "@tiptap/react";
import Suggestion, { type SuggestionOptions } from "@tiptap/suggestion";
import { filterSlashCommands, type SlashCommandItem } from "./slash-commands";
import { SlashCommandList, type SlashCommandListHandle } from "./slash-command-list";

const suggestion: Omit<SuggestionOptions<SlashCommandItem>, "editor"> = {
  char: "/",
  startOfLine: false,
  items: ({ query }) => filterSlashCommands(query),
  command: ({ editor, range, props }) => {
    props.command({ editor, range });
  },
  render: () => {
    let component: ReactRenderer<SlashCommandListHandle> | null = null;
    let popup: HTMLDivElement | null = null;

    const updatePosition = (clientRect: (() => DOMRect | null) | null | undefined) => {
      if (!popup || !clientRect) return;
      const rect = clientRect();
      if (!rect) return;
      const top = rect.bottom + window.scrollY + 6;
      const left = rect.left + window.scrollX;
      popup.style.top = `${top}px`;
      popup.style.left = `${left}px`;
    };

    return {
      onStart: (props) => {
        component = new ReactRenderer(SlashCommandList, {
          props: {
            items: props.items,
            command: (item: SlashCommandItem) => item.command({ editor: props.editor, range: props.range }),
          },
          editor: props.editor,
        });

        popup = document.createElement("div");
        popup.style.position = "absolute";
        popup.style.zIndex = "60";
        popup.appendChild(component.element);
        document.body.appendChild(popup);
        updatePosition(props.clientRect);
      },
      onUpdate: (props) => {
        component?.updateProps({
          items: props.items,
          command: (item: SlashCommandItem) => item.command({ editor: props.editor, range: props.range }),
        });
        updatePosition(props.clientRect);
      },
      onKeyDown: (props) => {
        return component?.ref?.onKeyDown(props) ?? false;
      },
      onExit: () => {
        popup?.remove();
        component?.destroy();
        popup = null;
        component = null;
      },
    };
  },
};

export const SlashCommand = Extension.create({
  name: "slashCommand",

  addOptions() {
    return {
      suggestion,
    };
  },

  addProseMirrorPlugins() {
    return [
      Suggestion({
        editor: this.editor,
        ...this.options.suggestion,
      }),
    ];
  },
});
