"use client";
import { useEffect } from "react";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { mergeRegister } from "@lexical/utils";
import { $createTextNode, $isElementNode, $isLineBreakNode, $isTextNode, TextNode } from "lexical";
import { $createAutoLinkNode, $isAutoLinkNode, $isLinkNode, AutoLinkNode } from "../../../nodes/link-nodes/index.js";
import invariant from "../../../shared/invariant.js";
function customAttrs(attrs) {
    if (null == attrs || 'internal' === attrs.linkType) return null;
    return attrs;
}
function createLinkMatcherWithRegExp(regExp, urlTransformer = (text)=>text) {
    return (text)=>{
        const match = regExp.exec(text);
        if (null === match) return null;
        return {
            index: match.index,
            length: match[0].length,
            text: match[0],
            url: urlTransformer(text)
        };
    };
}
function findFirstMatch(text, matchers) {
    for(let i = 0; i < matchers.length; i++){
        const match = matchers[i](text);
        if (null != match) return match;
    }
    return null;
}
const PUNCTUATION_OR_SPACE = /[.,;\s]/;
function isSeparator(char) {
    return PUNCTUATION_OR_SPACE.test(char);
}
function endsWithSeparator(textContent) {
    return isSeparator(textContent[textContent.length - 1]);
}
function startsWithSeparator(textContent) {
    return isSeparator(textContent[0]);
}
function isPreviousNodeValid(node) {
    let previousNode = node.getPreviousSibling();
    if ($isElementNode(previousNode)) previousNode = previousNode.getLastDescendant();
    return null === previousNode || $isLineBreakNode(previousNode) || $isTextNode(previousNode) && endsWithSeparator(previousNode.getTextContent());
}
function isNextNodeValid(node) {
    let nextNode = node.getNextSibling();
    if ($isElementNode(nextNode)) nextNode = nextNode.getFirstDescendant();
    return null === nextNode || $isLineBreakNode(nextNode) || $isTextNode(nextNode) && startsWithSeparator(nextNode.getTextContent());
}
function isContentAroundIsValid(matchStart, matchEnd, text, node) {
    const contentBeforeIsValid = matchStart > 0 ? isSeparator(text[matchStart - 1]) : isPreviousNodeValid(node);
    if (!contentBeforeIsValid) return false;
    const contentAfterIsValid = matchEnd < text.length ? isSeparator(text[matchEnd]) : isNextNodeValid(node);
    return contentAfterIsValid;
}
function handleLinkCreation(node, matchers, onChange) {
    const nodeText = node.getTextContent();
    let text = nodeText;
    let invalidMatchEnd = 0;
    let remainingTextNode = node;
    let match = null;
    match = findFirstMatch(text, matchers);
    while(null != match && null !== match){
        const matchStart = match.index;
        const matchLength = match.length;
        const matchEnd = matchStart + matchLength;
        const isValid = isContentAroundIsValid(invalidMatchEnd + matchStart, invalidMatchEnd + matchEnd, nodeText, node);
        if (isValid) {
            let linkTextNode;
            if (invalidMatchEnd + matchStart === 0) [linkTextNode, remainingTextNode] = remainingTextNode.splitText(invalidMatchEnd + matchLength);
            else [, linkTextNode, remainingTextNode] = remainingTextNode.splitText(invalidMatchEnd + matchStart, invalidMatchEnd + matchStart + matchLength);
            const attributes = {
                linkType: 'custom',
                url: match.url,
                ...match.attributes
            };
            const linkNode = $createAutoLinkNode({
                attributes
            });
            const textNode = $createTextNode(match.text);
            textNode.setFormat(linkTextNode.getFormat());
            textNode.setDetail(linkTextNode.getDetail());
            linkNode.append(textNode);
            linkTextNode.replace(linkNode);
            onChange(match.url, null);
            invalidMatchEnd = 0;
        }
        text = text.substring(matchEnd);
        match = findFirstMatch(text, matchers);
    }
}
function handleLinkEdit(linkNode, matchers, onChange) {
    const children = linkNode.getChildren();
    const childrenLength = children.length;
    for(let i = 0; i < childrenLength; i++){
        const child = children[i];
        if (!$isTextNode(child) || !child.isSimpleText()) {
            replaceWithChildren(linkNode);
            onChange(null, customAttrs(linkNode.getAttributes())?.url ?? null);
            return;
        }
    }
    const text = linkNode.getTextContent();
    const match = findFirstMatch(text, matchers);
    if (null === match || match.text !== text) {
        replaceWithChildren(linkNode);
        onChange(null, customAttrs(linkNode.getAttributes())?.url ?? null);
        return;
    }
    if (!isPreviousNodeValid(linkNode) || !isNextNodeValid(linkNode)) {
        replaceWithChildren(linkNode);
        onChange(null, customAttrs(linkNode.getAttributes())?.url ?? null);
        return;
    }
    const url = customAttrs(linkNode.getAttributes())?.url;
    if (url !== match?.url) {
        const attrs = customAttrs(linkNode.getAttributes());
        if (null != attrs) {
            attrs.url = match?.url;
            linkNode.setAttributes(attrs);
            onChange(match.url, url ?? null);
        }
    }
    if (null != match.attributes) {
        const rel = linkNode.getAttributes().rel;
        if (rel !== match.attributes.rel) {
            const attrs = linkNode.getAttributes();
            attrs.rel = match.attributes.rel ?? null;
            linkNode.setAttributes(attrs);
            onChange(match.attributes.rel ?? null, rel ?? null);
        }
    }
}
function handleBadNeighbors(textNode, matchers, onChange) {
    const previousSibling = textNode.getPreviousSibling();
    const nextSibling = textNode.getNextSibling();
    const text = textNode.getTextContent();
    if ($isAutoLinkNode(previousSibling) && !startsWithSeparator(text)) {
        previousSibling.append(textNode);
        handleLinkEdit(previousSibling, matchers, onChange);
        onChange(null, customAttrs(previousSibling.getAttributes())?.url ?? null);
    }
    if ($isAutoLinkNode(nextSibling) && !endsWithSeparator(text)) {
        replaceWithChildren(nextSibling);
        handleLinkEdit(nextSibling, matchers, onChange);
        onChange(null, customAttrs(nextSibling.getAttributes())?.url ?? null);
    }
}
function replaceWithChildren(node) {
    const children = node.getChildren();
    const childrenLength = children.length;
    for(let j = childrenLength - 1; j >= 0; j--)node.insertAfter(children[j]);
    node.remove();
    return children.map((child)=>child.getLatest());
}
function useAutoLink(editor, matchers, onChange) {
    useEffect(()=>{
        if (!editor.hasNodes([
            AutoLinkNode
        ])) invariant(false, 'LexicalAutoLinkPlugin: AutoLinkNode not registered on editor');
        const onChangeWrapped = (url, prevUrl)=>{
            if (null != onChange) onChange(url, prevUrl);
        };
        return mergeRegister(editor.registerNodeTransform(TextNode, (textNode)=>{
            const parent = textNode.getParentOrThrow();
            const previous = textNode.getPreviousSibling();
            if ($isAutoLinkNode(parent)) handleLinkEdit(parent, matchers, onChangeWrapped);
            else if (!$isLinkNode(parent)) {
                if (textNode.isSimpleText() && (startsWithSeparator(textNode.getTextContent()) || !$isAutoLinkNode(previous))) handleLinkCreation(textNode, matchers, onChangeWrapped);
                handleBadNeighbors(textNode, matchers, onChangeWrapped);
            }
        }));
    }, [
        editor,
        matchers,
        onChange
    ]);
}
function AutoLinkPlugin({ matchers, onChange }) {
    const [editor] = useLexicalComposerContext();
    useAutoLink(editor, matchers, onChange);
    return null;
}
export { AutoLinkPlugin, createLinkMatcherWithRegExp };
