import { type MentionOptions } from '@tiptap/extension-mention';
import { ReactRenderer } from '@tiptap/react';
import tippy, {
  type Placement,
  type Instance as TippyInstance,
} from 'tippy.js';

import { type Member } from 'src/pages/dashboard/Conversations/types';

import MentionUserList, { type MentionUserListRef } from './MentionUserList';

export type MentionSuggestion = {
  id: string;
  mentionLabel: string;
};

/**
 * Workaround for the current typing incompatibility between Tippy.js and Tiptap
 * Suggestion utility.
 *
 * @see https://github.com/ueberdosis/tiptap/issues/2795#issuecomment-1160623792
 *
 * Adopted from
 * https://github.com/Doist/typist/blob/a1726a6be089e3e1452def641dfcfc622ac3e942/stories/typist-editor/constants/suggestions.ts#L169-L186
 */
const DOM_RECT_FALLBACK: DOMRect = {
  bottom: 0,
  height: 0,
  left: 0,
  right: 0,
  top: 0,
  width: 0,
  x: 0,
  y: 0,
  toJSON() {
    return {};
  },
};

export const createMentionOptions = (
  members?: Member[],
  placement: Placement = 'bottom-start',
  isPopper: boolean = false,
): MentionOptions['suggestion'] => {
  return {
    render: () => {
      let component: ReactRenderer<MentionUserListRef> | undefined;
      let popup: TippyInstance | undefined;
      let isCancelled = false;

      return {
        onStart: props => {
          isCancelled = false;
          component = new ReactRenderer(MentionUserList, {
            props: {
              ...props,
              externalUsers: members,
              isPopper,
            },
            editor: props.editor,
          });

          if (!props.editor.isInitialized) {
            return;
          }

          popup = tippy(document.body, {
            getReferenceClientRect: () =>
              props.clientRect?.() ?? DOM_RECT_FALLBACK,
            appendTo: () =>
              props.editor.options.element.parentElement || document.body,
            content: component.element,
            showOnCreate: true,
            interactive: true,
            trigger: 'manual',
            placement,
            maxWidth: 'none',
            onCreate(instance) {
              if (instance.popper) {
                instance.popper.id = 'MentionList';
              }
            },
            ...(isPopper && {
              popperOptions: {
                strategy: 'fixed',
                modifiers: [
                  {
                    name: 'popperPosition',
                    options: {
                      boundary: 'viewport',
                      altBoundary: true,
                      tether: false,
                    },
                  },
                  {
                    name: 'flip',
                    options: {
                      fallbackPlacements: [
                        'top-start',
                        'top-end',
                        'bottom-start',
                        'bottom-end',
                        'left-start',
                        'right-start',
                        'left-end',
                        'right-end',
                      ],
                      allowedAutoPlacements: ['top', 'bottom', 'left', 'right'],
                      flipVariations: true,
                    },
                  },
                ],
              },
              zIndex: 1400,
            }),
          });
        },

        onUpdate(props) {
          if (isCancelled) {
            return;
          }

          component?.updateProps(props);

          popup?.setProps({
            getReferenceClientRect: () =>
              props.clientRect?.() ?? DOM_RECT_FALLBACK,
          });
        },

        onKeyDown(props) {
          if (props.event.key === 'Escape') {
            isCancelled = true;
            popup?.hide();

            return true;
          }

          if (isCancelled || !component?.ref) {
            return false;
          }

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

        onExit() {
          popup?.destroy();
          component?.destroy();
          popup = undefined;
          component = undefined;
          isCancelled = false;
        },
      };
    },
  };
};
