import { InputRule } from 'prosemirror-inputrules';
import { Fragment } from 'prosemirror-model';
import { TextSelection } from 'prosemirror-state';
import { codeType } from "../extensions/markdown/specs.js";
import { isFunction } from "../lodash.js";
import { isMarkActive } from "./marks.js";
// TODO: remove explicit import from code extension
export function hasCodeMark(state, _match, start, end) {
    // TODO: remove explicit import from code extension
    const codeMarkType = codeType(state.schema);
    if (isMarkActive(state, codeMarkType))
        return true;
    if (state.doc.rangeHasMark(start, end, codeMarkType))
        return true;
    return false;
}
export { textblockTypeInputRule, wrappingInputRule } from "./rulebuilders.js";
function getMarksBetween(start, end, state) {
    let marks = [];
    state.doc.nodesBetween(start, end, (node, pos) => {
        marks = [
            ...marks,
            ...node.marks.map((mark) => ({
                start: pos,
                end: pos + node.nodeSize,
                mark,
            })),
        ];
    });
    return marks;
}
function escapeRegex(string) {
    // eslint-disable-next-line no-useless-escape
    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
// TODO: link has a custom input rule handler, think about generalizing
export function markInputRule(rule, markType, getAttrs) {
    let regexp;
    if (rule instanceof RegExp) {
        regexp = rule;
    }
    else {
        const open = escapeRegex(rule.open);
        const close = escapeRegex(rule.close);
        const ignoreBetween = escapeRegex(rule.ignoreBetween || '');
        regexp = new RegExp(`(?:${open})` +
            (ignoreBetween ? `([^\\s${ignoreBetween}]+)` : `([\\S]+)`) +
            `(?:${close})\\s$`);
    }
    return new InputRule(regexp, (state, match, start, end) => {
        // handle the rule only if is start of line or there is a space before "open" symbols
        if (match.index > 0) {
            const re = match;
            if (re.input[re.index - 1] !== ' ')
                return null;
        }
        if (hasCodeMark(state, match, start, end))
            return null;
        const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
        const { tr } = state;
        const m = match.length - 1;
        let markEnd = end;
        let markStart = start;
        if (match[m]) {
            const matchStart = start + match[0].indexOf(match[m - 1]);
            const matchEnd = matchStart + match[m - 1].length - 1;
            const textStart = matchStart + match[m - 1].lastIndexOf(match[m]);
            const textEnd = textStart + match[m].length;
            const marksBetween = getMarksBetween(start, end, state);
            const excludedMarks = marksBetween
                .filter((item) => item.mark.type.excludes(markType))
                .filter((item) => item.end > matchStart);
            if (excludedMarks.length) {
                return null;
            }
            if (textEnd < matchEnd) {
                tr.delete(textEnd, matchEnd);
            }
            if (textStart > matchStart) {
                tr.delete(matchStart, textStart);
            }
            markStart = matchStart;
            markEnd = markStart + match[m].length;
        }
        tr.addMark(markStart, markEnd, markType.create(attrs));
        tr.removeStoredMark(markType);
        return tr;
    });
}
export function nodeInputRule(regexp, fragment, selectionOffset = 0) {
    return new InputRule(regexp, (state, match, start, end) => {
        if (hasCodeMark(state, match, start, end))
            return null;
        const [matchStr] = match;
        const { tr } = state;
        if (matchStr) {
            tr.replaceWith(start - 1, end, isFunction(fragment)
                ? fragment(matchStr) ?? Fragment.empty
                : fragment ?? Fragment.empty).setSelection(new TextSelection(tr.doc.resolve(start + selectionOffset)));
        }
        return tr;
    });
}
export function inlineNodeInputRule(regexp, fragment) {
    return new InputRule(regexp, (state, match, start, end) => {
        if (hasCodeMark(state, match, start, end))
            return null;
        const [matchStr] = match;
        const { tr } = state;
        if (matchStr) {
            tr.replaceWith(start, end, fragment(matchStr) ?? Fragment.empty).setSelection(new TextSelection(tr.doc.resolve(end)));
        }
        return tr;
    });
}
//# sourceMappingURL=inputrules.js.map
