import { TIPTAP_EMBED_TYPE, TIPTAP_LINK_PREVIEW_TYPE } from "./../../ricos-content/index.js";
import { convertBlockDataToRicos } from "./../../ricos-content/lib/convert-block-data-to-ricos.js";
import {
  createRicosFunctionalExtension,
  createRicosNodeExtension
} from "./../../plugin-commons/index.js";
import {
  createLinkPreviewData,
  fetchLinkPreview,
  shouldAddEmbed,
  shouldAddLinkPreview
} from "../lib/utils.js";
import { LINK_PREVIEW_TYPE } from "../types.js";
import { Embed } from "./embed.js";
import { LinkPreview } from "./link-preview.js";
const urlRegex = (
  // oxlint-disable-next-line max-len
  /(?:https?:\/\/)?(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi
);
const urlRegexExact = new RegExp("^" + urlRegex.source + "$", "gi");
const isLinkOnlyBlock = ({
  pos,
  fromShortcut
}) => fromShortcut ? pos.parent.childCount === 1 && pos.nodeBefore?.text?.search(urlRegexExact) !== -1 : pos.parent.text?.search(urlRegexExact) !== -1;
const getLinkDeletionRange = ({
  pos,
  fromShortcut
}) => {
  const parentText = pos.parent.textContent;
  const match = parentText.match(urlRegexExact);
  if (match) {
    const startIndex = parentText.indexOf(match[0]);
    return {
      from: pos.start() + startIndex,
      to: pos.start() + startIndex + match[0].length
    };
  }
  if (fromShortcut) {
    return {
      from: pos.pos - (pos.nodeBefore?.nodeSize || 0),
      to: pos.pos + (pos.nodeAfter?.nodeSize || 0)
    };
  }
  return { from: pos.start(), to: pos.end() };
};
const extractUrlFromNode = (node) => {
  const linkMark = node.marks.find((mark) => mark.type.name === "LINK");
  const rawUrl = linkMark?.attrs?.link?.url ?? linkMark?.attrs?.href;
  return rawUrl?.trim();
};
const addLinkPreview = (config) => (
  // @ts-expect-error
  ({ editor, tr }) => {
    tr?.setMeta("preventDispatch", true);
    const pos = editor.state.selection.$anchor;
    const {
      enableEmbed = true,
      enableLinkPreview = true,
      fetchData,
      link: linkConfig,
      fromShortcut = true
    } = config || {};
    if (pos.nodeBefore?.text && isLinkOnlyBlock({ pos, fromShortcut }) && fetchData && (enableEmbed || enableLinkPreview)) {
      let isPreviewAdded = false;
      const { target, rel } = linkConfig || {};
      const url = extractUrlFromNode(pos.nodeBefore);
      if (!url) {
        return false;
      }
      insertPreviewPlaceholder(editor, url, pos, fromShortcut);
      fetchLinkPreview(fetchData, url).then((linkPreviewData) => {
        const shouldEmbed = shouldAddEmbed(
          linkPreviewData.html,
          enableEmbed,
          linkPreviewData.fixedUrl
        );
        const shouldPreview = shouldAddLinkPreview(linkPreviewData.title, enableLinkPreview);
        if (!shouldEmbed && !shouldPreview) {
          return;
        }
        isPreviewAdded = true;
        return createLinkPreviewData({ url, target, rel }, linkPreviewData, config).then(
          (linkPreviewComponentData) => {
            const convertedLinkPreviewData = convertBlockDataToRicos(
              LINK_PREVIEW_TYPE,
              linkPreviewComponentData
            );
            const placeholder = findPlaceholderNode(editor, url);
            if (!placeholder) {
              return;
            }
            if (shouldEmbed) {
              replacePlaceholderWithEmbed(editor, placeholder, convertedLinkPreviewData);
            } else {
              updatePlaceholderAttributes(editor, placeholder.id, {
                ...convertedLinkPreviewData,
                loading: false,
                error: false
              });
            }
          }
        );
      }).catch(() => {
        const placeholder = findPlaceholderNode(editor, url);
        if (placeholder) {
          updatePlaceholderAttributes(editor, placeholder.id, {
            loading: false,
            error: true,
            link: { url }
          });
        }
      }).finally(() => {
      });
      return isPreviewAdded;
    }
    return false;
  }
);
const insertPreviewPlaceholder = (editor, url, pos, fromShortcut) => {
  editor.chain().focus().deleteRange(getLinkDeletionRange({ pos, fromShortcut })).insertContent([
    { type: TIPTAP_LINK_PREVIEW_TYPE, attrs: { link: { url }, loading: true, error: false } },
    { type: "PARAGRAPH" }
  ]).run();
};
const findPlaceholderNode = (editor, url) => {
  let placeholderNode = null;
  editor.state.doc.descendants((node, pos) => {
    if (node.type.name === TIPTAP_LINK_PREVIEW_TYPE && node.attrs.link?.url === url && node.attrs.loading === true) {
      placeholderNode = { id: node.attrs.id, pos, size: node.nodeSize };
      return false;
    }
    return true;
  });
  return placeholderNode;
};
const replacePlaceholderWithEmbed = (editor, placeholder, embedAttrs) => {
  editor.state.doc.descendants((node, pos) => {
    if (node.type.name === TIPTAP_LINK_PREVIEW_TYPE && node.attrs.id === placeholder.id) {
      editor.commands.deleteRange({ from: pos, to: pos + node.nodeSize });
      return false;
    }
    return true;
  });
  editor.chain().focus().insertContent([{ type: TIPTAP_EMBED_TYPE, attrs: embedAttrs }, { type: "PARAGRAPH" }]).run();
};
const updatePlaceholderAttributes = (editor, nodeId, attrs) => {
  editor.chain().setNodeAttrsById(nodeId, attrs).setMeta("addToHistory", false).run();
};
const tiptapExtensions = [
  createRicosNodeExtension({
    name: TIPTAP_LINK_PREVIEW_TYPE,
    groups: [
      "draggable",
      "not-overtypable",
      { group: "resizable", options: { expandToFullWidthOnOverdrag: true } }
    ],
    type: "react-node",
    reconfigure: (config, _extensions, _props, settings) => ({
      ...config,
      addOptions: () => settings
    }),
    Component: LinkPreview,
    createExtensionConfig() {
      return {
        group: "block smartBlockCellContent layoutCellContent collapsibleListItemBodyContent tableCellContent",
        selectable: true,
        draggable: true,
        name: this.name,
        addAttributes: () => ({
          containerData: null,
          link: null,
          title: null,
          thumbnailUrl: null,
          description: null,
          html: null,
          styles: null,
          loading: {
            default: false
          },
          error: {
            default: false
          }
        }),
        addCommands() {
          return {
            setLinkPreviewLoading: (loading) => ({ commands }) => {
              return commands.setNode("linkPreview", { loading });
            },
            setLinkPreviewError: (error) => ({ commands }) => {
              return commands.setNode("linkPreview", { error });
            },
            setLinkPreview: (attributes) => ({ commands }) => {
              return commands.setNode("linkPreview", attributes);
            }
          };
        }
      };
    }
  }),
  createRicosNodeExtension({
    name: TIPTAP_EMBED_TYPE,
    groups: ["overlay", "draggable", "not-overtypable"],
    type: "react-node",
    reconfigure: (config, _extensions, _props, settings) => ({
      ...config,
      addOptions: () => settings
    }),
    Component: Embed,
    createExtensionConfig() {
      return {
        group: "block smartBlockCellContent layoutCellContent collapsibleListItemBodyContent tableCellContent",
        selectable: true,
        draggable: true,
        name: this.name,
        addAttributes: () => ({
          containerData: null,
          oembed: null,
          src: null
        })
      };
    }
  }),
  createRicosFunctionalExtension({
    type: "extension",
    groups: ["shortcuts-enabled"],
    name: "linkPreviewEnter",
    reconfigure: (config, _extensions, _props, settings, services) => ({
      ...config,
      addKeyboardShortcuts() {
        const enableEnterToConvertLinks = settings.enableEnterToConvertLinks ?? true;
        if (!enableEnterToConvertLinks) {
          return {};
        }
        const newLine = addLinkPreview(settings);
        const tiptapKeyboardShortcuts = {
          Enter: newLine
        };
        const newLineShortcuts = [];
        services.shortcuts.shortcuts.forEach((shortcut) => {
          if (shortcut.shortcut.name === "NewLine") {
            newLineShortcuts.push(shortcut);
          }
        });
        newLineShortcuts.forEach((newLineShortcut) => {
          const modifier = newLineShortcut.keys.macOs.modifiers[0];
          const key = newLineShortcut.keys.macOs.key;
          const shortcutKey = modifier ? `${modifier}-${key}` : key;
          tiptapKeyboardShortcuts[shortcutKey] = newLine;
        });
        return {
          ...tiptapKeyboardShortcuts
        };
      },
      addCommands() {
        const linkPreviewCommand = addLinkPreview({
          ...settings,
          fromShortcut: false
        });
        return {
          createLinkPreview: () => linkPreviewCommand
        };
      }
    }),
    createExtensionConfig() {
      return {
        name: this.name,
        priority: 101
      };
    }
  })
];
export {
  tiptapExtensions
};
//# sourceMappingURL=tiptap.js.map
