import { useLexicalComposerContext } from "./LexicalComposerContext";
import { useMenuAnchorRef, LexicalMenu, MenuOption, } from "./shared/LexicalMenu";
import { $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_LOW, createCommand, getDOMSelection, } from "lexical";
import { createEffect, on, createSignal, Show, startTransition, onCleanup, onMount, } from "solid-js";
export const PUNCTUATION = "\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;";
const scrollIntoViewIfNeeded = (target) => {
    const typeaheadContainerNode = document.getElementById("typeahead-menu");
    if (!typeaheadContainerNode)
        return;
    const typeaheadRect = typeaheadContainerNode.getBoundingClientRect();
    if (typeaheadRect.top + typeaheadRect.height > window.innerHeight) {
        typeaheadContainerNode.scrollIntoView({
            block: "center",
        });
    }
    if (typeaheadRect.top < 0) {
        typeaheadContainerNode.scrollIntoView({
            block: "center",
        });
    }
    target.scrollIntoView({ block: "nearest" });
};
function getTextUpToAnchor(selection) {
    const anchor = selection.anchor;
    if (anchor.type !== "text") {
        return null;
    }
    const anchorNode = anchor.getNode();
    if (!anchorNode.isSimpleText()) {
        return null;
    }
    const anchorOffset = anchor.offset;
    return anchorNode.getTextContent().slice(0, anchorOffset);
}
function tryToPositionRange(leadOffset, range, editorWindow) {
    const domSelection = getDOMSelection(editorWindow);
    if (domSelection === null || !domSelection.isCollapsed) {
        return false;
    }
    const anchorNode = domSelection.anchorNode;
    const startOffset = leadOffset;
    const endOffset = domSelection.anchorOffset;
    if (anchorNode == null || endOffset == null) {
        return false;
    }
    try {
        range.setStart(anchorNode, startOffset);
        range.setEnd(anchorNode, endOffset);
    }
    catch (error) {
        return false;
    }
    return true;
}
function getQueryTextForSearch(editor) {
    let text = null;
    editor.getEditorState().read(() => {
        const selection = $getSelection();
        if (!$isRangeSelection(selection)) {
            return;
        }
        text = getTextUpToAnchor(selection);
    });
    return text;
}
function isSelectionOnEntityBoundary(editor, offset) {
    if (offset !== 0) {
        return false;
    }
    return editor.getEditorState().read(() => {
        const selection = $getSelection();
        if ($isRangeSelection(selection)) {
            const anchor = selection.anchor;
            const anchorNode = anchor.getNode();
            const prevSibling = anchorNode.getPreviousSibling();
            return $isTextNode(prevSibling) && prevSibling.isTextEntity();
        }
        return false;
    });
}
// Got from https://stackoverflow.com/a/42543908/2013580
export function getScrollParent(element, includeHidden) {
    let style = getComputedStyle(element);
    const excludeStaticParent = style.position === "absolute";
    const overflowRegex = includeHidden
        ? /(auto|scroll|hidden)/
        : /(auto|scroll)/;
    if (style.position === "fixed") {
        return document.body;
    }
    for (let parent = element; (parent = parent.parentElement);) {
        style = getComputedStyle(parent);
        if (excludeStaticParent && style.position === "static") {
            continue;
        }
        if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
            return parent;
        }
    }
    return document.body;
}
export { useDynamicPositioning } from "./shared/LexicalMenu";
export const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND = createCommand("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");
export function useBasicTypeaheadTriggerMatch(trigger, { minLength = 1, maxLength = 75 }) {
    return (text) => {
        const validChars = "[^" + trigger + PUNCTUATION + "\\s]";
        const TypeaheadTriggerRegex = new RegExp("(^|\\s|\\()(" +
            "[" +
            trigger +
            "]" +
            "((?:" +
            validChars +
            "){0," +
            maxLength +
            "})" +
            ")$");
        const match = TypeaheadTriggerRegex.exec(text);
        if (match !== null) {
            const maybeLeadingWhitespace = match[1];
            const matchingString = match[3];
            if (matchingString.length >= minLength) {
                return {
                    leadOffset: match.index + maybeLeadingWhitespace.length,
                    matchingString,
                    replaceableString: match[2],
                };
            }
        }
        return null;
    };
}
export function LexicalTypeaheadMenuPlugin(props) {
    const [editor] = useLexicalComposerContext();
    const [resolution, setResolution] = createSignal(null);
    const anchorElementRef = useMenuAnchorRef(resolution, setResolution, props.anchorClassName, props.parent);
    const closeTypeahead = () => {
        if (props.onClose != null && resolution() !== null) {
            props.onClose();
        }
        setResolution(null);
    };
    const openTypeahead = (res) => {
        if (props.onOpen != null && resolution() === null) {
            props.onOpen(res);
        }
        setResolution(res);
    };
    createEffect(on(resolution, () => {
        const updateListener = () => {
            editor.getEditorState().read(() => {
                // Check if editor is in read-only mode
                if (!editor.isEditable()) {
                    closeTypeahead();
                    return;
                }
                const editorWindow = editor._window || window;
                const range = editorWindow.document.createRange();
                const selection = $getSelection();
                const text = getQueryTextForSearch(editor);
                if (!$isRangeSelection(selection) ||
                    !selection.isCollapsed() ||
                    text === null ||
                    range === null) {
                    closeTypeahead();
                    return;
                }
                const match = props.triggerFn(text, editor);
                props.onQueryChange(match ? match.matchingString : null);
                if (match !== null &&
                    !isSelectionOnEntityBoundary(editor, match.leadOffset)) {
                    const isRangePositioned = tryToPositionRange(match.leadOffset, range, editorWindow);
                    if (isRangePositioned !== null) {
                        startTransition(() => openTypeahead({
                            getRect: () => range.getBoundingClientRect(),
                            match,
                        }));
                        return;
                    }
                }
                closeTypeahead();
            });
        };
        const removeUpdateListener = editor.registerUpdateListener(updateListener);
        onCleanup(() => {
            removeUpdateListener();
        });
    }));
    onMount(() => {
        onCleanup(editor.registerEditableListener((isEditable) => {
            if (!isEditable) {
                closeTypeahead();
            }
        }));
    });
    return (<Show when={anchorElementRef.current && resolution() !== null && editor !== null}>
      <LexicalMenu close={closeTypeahead} resolution={resolution()} editor={editor} anchorElementRef={anchorElementRef} options={props.options} menuRenderFn={props.menuRenderFn} onSelectOption={props.onSelectOption} shouldSplitNodeWithQuery={true} commandPriority={props.commandPriority ?? COMMAND_PRIORITY_LOW} preselectFirstItem={props.preselectFirstItem ?? true}/>
    </Show>);
}
export { MenuOption };
