import { $getNodeByKey, $getSelection, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, createCommand, } from "lexical";
import { $isLinkNode, AutoLinkNode, LinkNode } from "@lexical/link";
import { useLexicalComposerContext } from "./LexicalComposerContext";
import { mergeRegister } from "@lexical/utils";
import { createEffect, createMemo, createSignal, Show, onCleanup, } from "solid-js";
import { LexicalNodeMenuPlugin, MenuOption } from "./LexicalNodeMenuPlugin";
export const URL_MATCHER = /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
export const INSERT_EMBED_COMMAND = createCommand("INSERT_EMBED_COMMAND");
export class AutoEmbedOption extends MenuOption {
    title;
    onSelect;
    constructor(title, options) {
        super(title);
        this.title = title;
        this.onSelect = options.onSelect.bind(this);
    }
}
export function LexicalAutoEmbedPlugin(props) {
    const [editor] = useLexicalComposerContext();
    const [nodeKey, setNodeKey] = createSignal(null);
    const [activeEmbedConfig, setActiveEmbedConfig] = createSignal(null);
    const reset = () => {
        setNodeKey(null);
        setActiveEmbedConfig(null);
    };
    const checkIfLinkNodeIsEmbeddable = async (key) => {
        const url = editor.getEditorState().read(() => {
            const linkNode = $getNodeByKey(key);
            if ($isLinkNode(linkNode)) {
                return linkNode.getURL();
            }
        });
        if (url === undefined) {
            return;
        }
        for (const embedConfig of props.embedConfigs) {
            const urlMatch = await Promise.resolve(embedConfig.parseUrl(url));
            if (urlMatch != null) {
                setActiveEmbedConfig(() => embedConfig);
                setNodeKey(key);
            }
        }
    };
    createEffect(() => {
        const listener = (nodeMutations, { updateTags, dirtyLeaves }) => {
            for (const [key, mutation] of nodeMutations) {
                if (mutation === "created" &&
                    updateTags.has("paste") &&
                    dirtyLeaves.size <= 3) {
                    checkIfLinkNodeIsEmbeddable(key);
                }
                else if (key === nodeKey()) {
                    reset();
                }
            }
        };
        onCleanup(mergeRegister(...[LinkNode, AutoLinkNode].map((Klass) => editor.registerMutationListener(Klass, (...args) => listener(...args), {
            skipInitialization: true,
        }))));
    });
    createEffect(() => {
        onCleanup(editor.registerCommand(INSERT_EMBED_COMMAND, (embedConfigType) => {
            const embedConfig = props.embedConfigs.find(({ type }) => type === embedConfigType);
            if (embedConfig) {
                props.onOpenEmbedModalForConfig(embedConfig);
                return true;
            }
            return false;
        }, COMMAND_PRIORITY_EDITOR));
    });
    const embedLinkViaActiveEmbedConfig = async () => {
        if (activeEmbedConfig() != null && nodeKey() != null) {
            const linkNode = editor.getEditorState().read(() => {
                const node = $getNodeByKey(nodeKey());
                if ($isLinkNode(node)) {
                    return node;
                }
                return null;
            });
            if ($isLinkNode(linkNode)) {
                const result = await Promise.resolve(activeEmbedConfig().parseUrl(linkNode.__url));
                if (result != null) {
                    editor.update(() => {
                        if (!$getSelection()) {
                            linkNode.selectEnd();
                        }
                        activeEmbedConfig().insertNode(editor, result);
                        if (linkNode.isAttached()) {
                            linkNode.remove();
                        }
                    });
                }
            }
        }
    };
    const options = createMemo(() => {
        return activeEmbedConfig() != null && nodeKey() != null
            ? props.getMenuOptions(activeEmbedConfig(), embedLinkViaActiveEmbedConfig, reset)
            : [];
    });
    const onSelectOption = (selectedOption, targetNode, closeMenu) => {
        editor.update(() => {
            selectedOption.onSelect(targetNode);
            closeMenu();
        });
    };
    return (<Show when={nodeKey() != null}>
      <LexicalNodeMenuPlugin nodeKey={nodeKey()} onClose={reset} onSelectOption={onSelectOption} options={options()} menuRenderFn={props.menuRenderFn} commandPriority={props.menuCommandPriority ?? COMMAND_PRIORITY_LOW}/>
    </Show>);
}
