import { Extension } from "@tiptap/react";
import { ReactRenderer } from "@tiptap/react";
import Suggestion from "@tiptap/suggestion";
import tippy, { type Instance as TippyInstance } from "tippy.js";
import {
  slashCommandItems,
  type SlashCommandItem,
} from "./slash-command-items";
import { SlashCommandList, type SlashCommandListRef } from "./SlashCommandList";

export const SlashCommands = Extension.create({
  name: "slashCommands",

  addStorage() {
    return {
      onSubpageCreate: null as ((editor: unknown) => void) | null,
      hideSubpage: false,
    };
  },

  addOptions() {
    return {
      suggestion: {
        char: "/",
        startOfLine: false,
        command: ({
          editor,
          range,
          props,
        }: {
          editor: ReturnType<typeof import("@tiptap/react").useEditor>;
          range: { from: number; to: number };
          props: SlashCommandItem;
        }) => {
          // Delete the slash command text
          editor.chain().focus().deleteRange(range).run();
          // Execute the selected command
          props.action(editor);
        },
        items: ({ query, editor }: { query: string; editor: ReturnType<typeof import("@tiptap/react").useEditor> }): SlashCommandItem[] => {
          let items = slashCommandItems.filter((item) =>
            item.label.toLowerCase().includes(query.toLowerCase()),
          );
          if (editor.storage.slashCommands?.hideSubpage) {
            items = items.filter((item) => !item.isSubpage);
          }
          return items;
        },
        render: () => {
          let component: ReactRenderer<SlashCommandListRef> | null = null;
          let popup: TippyInstance[] | null = null;

          return {
            onStart: (props: {
              editor: ReturnType<typeof import("@tiptap/react").useEditor>;
              clientRect: (() => DOMRect | null) | null;
              items: SlashCommandItem[];
              command: (item: SlashCommandItem) => void;
            }) => {
              component = new ReactRenderer(SlashCommandList, {
                props: {
                  items: props.items,
                  command: props.command,
                },
                editor: props.editor,
              });

              const getReferenceClientRect = props.clientRect;

              popup = tippy("body", {
                getReferenceClientRect: getReferenceClientRect as () => DOMRect,
                appendTo: () => document.body,
                content: component.element,
                showOnCreate: true,
                interactive: true,
                trigger: "manual",
                placement: "bottom-start",
              });
            },

            onUpdate: (props: {
              items: SlashCommandItem[];
              command: (item: SlashCommandItem) => void;
              clientRect: (() => DOMRect | null) | null;
            }) => {
              component?.updateProps({
                items: props.items,
                command: props.command,
              });

              if (popup?.[0]) {
                popup[0].setProps({
                  getReferenceClientRect:
                    props.clientRect as unknown as () => DOMRect,
                });
              }
            },

            onKeyDown: (props: { event: KeyboardEvent }) => {
              if (props.event.key === "Escape") {
                popup?.[0]?.hide();
                return true;
              }

              return component?.ref?.onKeyDown(props) ?? false;
            },

            onExit: () => {
              popup?.[0]?.destroy();
              component?.destroy();
            },
          };
        },
      },
    };
  },

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