import { EditorState, Plugin, PluginKey, TextSelection, Transaction, Selection } from 'prosemirror-state';
import { strings } from '@douyinfe/semi-foundation/aiChatInput/constants';
import { EditorView } from '@tiptap/pm/view';
/**
 * @param newState 
 * @returns 
 * handleZeroWidthCharLogic 用于插入零宽字符或者删除多余的零宽字符
 * 为什么需要插入零宽字符？
 *  1. 保证自定义节点前后的光标高度正常，光标高度和内容相关，解决自定义节点是最后一个节点，
 *     光标高度会和自定义节点占据高度一致，和文本中光标高度不一致的问题
 *  2. 保证对于可编辑的 inline 节点（比如 input-slot），为最后一个节点时候，光标可以聚焦到该节点后
 *  Why do we need to insert zero-width characters?
 *  1. Ensure that the cursor height before and after the custom node is normal.
 *  The cursor height is related to the content. Solve the problem that when the custom node is the last node，
 *  the cursor height will be consistent with the height occupied by the custom node, and inconsistent with the cursor height in the text.
 *  2. Ensure that for an editable inline node (such as input-slot), when it is the last node, the cursor can focus after the node.
 */

export function handleZeroWidthCharLogic(newState: EditorState) {
    let todoPositions = [];
    let { tr } = newState;
    newState.doc.descendants((node, pos, parent) => {
        if (node.type.name === 'paragraph' && node.childCount > 0) {
            const { lastChild, firstChild } = node;
            if (firstChild && firstChild.attrs.isCustomSlot) {
                // 如果第一个 child 是自定义节点，应该在自定义节点前添加零宽字符
                // If the first child is a custom node, a zero-width character should be added before the custom node.
                // 保证光标可以移动到第一个自定义节点前
                // Ensure that the cursor can move to the first custom node before.
                todoPositions.push(pos + 1);
            }

            if (lastChild && lastChild.attrs.isCustomSlot) {
                // 在段落末尾插入一个零宽字符, 避免当自定义节点是段落最后一个节点时候，光标无法移出
                // Insert a zero-width character at the end of the paragraph to prevent 
                // the cursor from being unable to move out when custom node is the last node of the paragraph.
                const paragraphEndPos = pos + node.nodeSize - 1;
                const prevChar = tr.doc.textBetween(paragraphEndPos - 1, paragraphEndPos, '', '');
                if (prevChar !== strings.ZERO_WIDTH_CHAR) {
                    todoPositions.push(paragraphEndPos);
                }
            }
            if (lastChild === firstChild && lastChild.isText && lastChild.text === strings.ZERO_WIDTH_CHAR) {
                todoPositions.push(['remove', pos + 1]);
            }
        }
        // 保证在 undo/通过 set 修改 content 时候，没有内容的 inputSlot 节点内部有零宽字符
        // Ensure that there are zero-width characters inside the inputSlot node without content when undoing/setting content
        // 保证 input-slot 节点可以正常显示
        // Ensure that the input-slot node can be displayed normally
        if (node.type.name === 'inputSlot' && node.content.size === 0) {
            todoPositions.push(pos + 1);
        }
        /**
        * 如果连续两个节点都是 custom slot，则需要在两个节点中间插入零宽字符，用于保证
        * - 对于 input-slot，光标可以移动到两个 input-slot 之间
        * - 对于其他的非输入类型的 custom-slot，光标高度正确
        */ 
        if (node.attrs.isCustomSlot) {
            let nodeIndex = -1;
            parent.forEach((child, offset, i) => {
                if (child === node) {
                    nodeIndex = i;
                }
            });
            if (nodeIndex > -1 && nodeIndex < parent.childCount - 1) {
                const nextSibling = parent.child(nodeIndex + 1);
                if (nextSibling.attrs.isCustomSlot) {
                    todoPositions.push(pos + node.nodeSize);
                }
            }
        }
    });
    if (todoPositions.length > 0) {
        // why sorting?
        // If you insert from the beginning, the newly inserted content will affect the position of the original record.
        todoPositions.sort((a, b) => {
            const aOrder = Array.isArray(a) ? a[1] : a;
            const bOrder = Array.isArray(b) ? b[1] : b;
            return bOrder - aOrder;
        }).forEach(insertPos => {
            if (Array.isArray(insertPos) && insertPos[0] === 'remove') {
                tr = tr.delete(insertPos[1], insertPos[1] + 1);
            } else {
                tr = tr.insertText(strings.ZERO_WIDTH_CHAR, insertPos, insertPos);
            }
        });
        return tr;
    }
    return null;
}

type SyntheticBackspacePatchedView = EditorView & {
    _semiSyntheticBackspaceGuard?: boolean;
    _semiOrigSomeProp?: typeof EditorView.prototype.someProp
};

/**
 * ProseMirror uses `document.createEvent("Event")` to synthesize a Backspace
 * during `readDOMChange()` when a DOM diff "looks like" a deletion. That replay
 * is not a real browser KeyboardEvent, but it still flows through `handleKeyDown`.
 */
function isSyntheticBackspaceEvent(event: KeyboardEvent) {
    return event?.type === 'keydown' &&
        event?.key === 'Backspace' &&
        event?.keyCode === 8 &&
        (event.isTrusted === false || !(event instanceof KeyboardEvent));
}

/**
 * Wrap `view.someProp("handleKeyDown")` so we can ignore only ProseMirror's
 * synthetic Backspace replay inside `inputSlot` while IME composition is active.
 *
 * The real user Backspace must still go through unchanged, otherwise slot
 * deletion, placeholder recovery and cursor movement regressions come back.
 */
function installSyntheticBackspaceGuard(view: EditorView) {
    const patchedView = view as SyntheticBackspacePatchedView;
    if (patchedView._semiSyntheticBackspaceGuard) {
        return;
    }

    const origSomeProp = view.someProp.bind(view);
    patchedView._semiSyntheticBackspaceGuard = true;
    patchedView._semiOrigSomeProp = origSomeProp;

    view.someProp = function (propName: any, f?: any) {
        if (propName !== 'handleKeyDown' || typeof f !== 'function') {
            return origSomeProp(propName, f);
        }

        return origSomeProp(propName, (prop: any) => {
            if (typeof prop !== 'function') {
                return f(prop);
            }

            return f((innerView: EditorView, event: KeyboardEvent) => {
                // ProseMirror may replay a synthetic Backspace from readDOMChange().
                // Ignore only that replay inside inputSlot while IME is active.
                if (
                    innerView.composing &&
                    innerView.state.selection.$from.parent.type.name === 'inputSlot' &&
                    isSyntheticBackspaceEvent(event)
                ) {
                    return false;
                }

                return prop(innerView, event);
            });
        });
    } as any;
}

/**
 * Restore the original `someProp` implementation when the plugin view unmounts.
 */
function removeSyntheticBackspaceGuard(view: EditorView) {
    const patchedView = view as SyntheticBackspacePatchedView;
    if (!patchedView._semiSyntheticBackspaceGuard || !patchedView._semiOrigSomeProp) {
        return;
    }

    view.someProp = patchedView._semiOrigSomeProp;
    delete patchedView._semiOrigSomeProp;
    delete patchedView._semiSyntheticBackspaceGuard;
}

export function ensureTrailingText(schema: any) {
    return new Plugin({
        view(view) {
            installSyntheticBackspaceGuard(view);

            return {
                destroy() {
                    removeSyntheticBackspaceGuard(view);
                },
            };
        },
        appendTransaction(transactions, oldState, newState) {
            if (transactions.some(tr => tr.getMeta('composition'))) {
                return null;
            }
            if (transactions.some(tr => tr.getMeta(strings.DELETABLE))) {
                return null;
            }
            const docChanged = transactions.some(tr => tr.docChanged);
            if (!docChanged) return null;
            return handleZeroWidthCharLogic(newState);
        },
    });
}

export function keyDownHandlePlugin(schema: any) {
    return new Plugin({
        key: new PluginKey('prevent-empty-inline-node'),
        props: {
            handleKeyDown(view, event) {
                if (view.composing) {
                    return false;
                }
                const { state, dispatch } = view;
                const { selection } = state;
                const { $from, $to } = selection;
                const node = $from.node();
                if (event.key === 'ArrowLeft' && node.type.name !== 'inputSlot') {
                    if ($from.nodeBefore && $from.nodeBefore.isText && $from.nodeBefore.text) {
                        if ($from.nodeBefore.text === strings.ZERO_WIDTH_CHAR) {
                            // 获取零宽字符前的节点
                            // Get the node before the zero-width character
                            const parent = $from.parent;
                            const index = $from.index();
                            if (index >= 2) {
                                /**
                                 * 判断条件: 节点顺序为[···、customSlot、零宽字符、光标、····],按下 arrowLeft
                                 * - 如果 custom slot 为 input-slot， 则光标跳到 input-slot 的最后一个可聚焦位置
                                 * - 如果 custom slot 为其他不可编辑的 slot， 则光标调整到 custom slot 之前，注：不可编辑的节点大小为 1
                                 */
                                const secondBeforeCursorNode = parent.child(index - 2);
                                if (secondBeforeCursorNode.attrs.isCustomSlot) {
                                    // The end of the content in the inputSlot node
                                    const nextCursorPos = $from.pos - 2;
                                    dispatch(state.tr.setSelection(TextSelection.create(state.doc, nextCursorPos)));
                                    event.preventDefault();
                                    return true;
                                }
                            } else if (index === 1 && $from.pos !== 0) {
                                /**
                                 * 判断条件: 节点顺序为[前一个 Paragraph、换行、零宽字符、光标、····],按下 arrowLeft
                                 * 结果: [前一个 Paragraph、光标、换行、零宽字符、 ····]
                                 */
                                const nextCursorPos = $from.before() - 1;
                                nextCursorPos > 0 && dispatch(state.tr.setSelection(TextSelection.create(state.doc, nextCursorPos)));
                                event.preventDefault();
                                return true;
                            }
                        } else if ($from.nodeBefore.text.endsWith(strings.ZERO_WIDTH_CHAR)) {
                            // Backup，当零宽字符出现在 text 节点中
                            const nextCursorPos = $from.pos - 2;
                            dispatch(state.tr.setSelection(TextSelection.create(state.doc, nextCursorPos)));
                            event.preventDefault();
                            return true;
                        }
                    }


                }
                if (event.key === 'ArrowRight' && node.type.name !== 'inputSlot') {
                    if ($from.nodeAfter && $from.nodeAfter.isText) {
                        if ($from.nodeAfter.text === strings.ZERO_WIDTH_CHAR) {
                            /**
                             * 判断条件: 节点顺序为[···、光标、零宽字符、customSlot、····],按下 arrowRight
                             * - 如果 custom slot 为 input-slot， 则光标跳到 input-slot 的第一个一个可聚焦位置
                             * - 如果 custom slot 为其他不可编辑的 slot， 则光标调整到 custom slot 之后
                             */
                            // 获取零宽字符后的节点
                            // Get the node before the zero-width character
                            const parent = $from.parent;
                            const index = $from.index();
                            if (index < parent.children.length - 1) {
                                const secondAfterCursorNode = parent.child(index + 1);
                                if (secondAfterCursorNode.attrs.isCustomSlot) {
                                    // The starting position of the input-slot node
                                    const newPos = $from.pos + 2;
                                    dispatch(state.tr.setSelection(TextSelection.create(state.doc, newPos)));
                                    event.preventDefault();
                                    return true;
                                }
                            } else if (index === parent.children.length - 1 && state.doc.lastChild !== node ) {
                                /**
                                 * 判断条件: 节点顺序为[···光标、零宽字符、换行、下一个 paragraphph···],按下 arrowLeft
                                 * 结果: [···零宽字符、换行、光标、下一个 paragraphph···]
                                 */
                                const nextCursorPos = $from.after() + 1;
                                dispatch(state.tr.setSelection(TextSelection.create(state.doc, nextCursorPos)));
                                event.preventDefault();
                                return true;
                            }
                        } else if ($from.nodeBefore && $from.nodeBefore.isText && $from.nodeBefore.text.startsWith(strings.ZERO_WIDTH_CHAR)) {
                            // Backup，当零宽字符出现在 text 节点中
                            const nextCursorPos = $from.pos + 2;
                            dispatch(state.tr.setSelection(TextSelection.create(state.doc, nextCursorPos)));
                            event.preventDefault();
                            return true;
                        }
                    }
                }

                if (event.key === 'Backspace' && selection.empty) {
                    const beforeNode = $from.nodeBefore;
                    const afterNode = $from.nodeAfter;
                    /**
                     * [长度为 1 的普通文本、光标、 customSlot] ---按下删除按键--->[光标、customSlot]
                     * 专用于处理 custom slot 前为一个文本节点，且文本节点中长度为1时候，文本删除不掉的情况
                     */
                    if (
                        $from.nodeBefore && $from.nodeBefore.isText && 
                        $from.nodeBefore.text?.length === 1 && $from.nodeBefore.text !== strings.ZERO_WIDTH_CHAR &&
                        $from.nodeAfter && $from.nodeAfter.attrs.isCustomSlot
                    ) {
                        const begin = $from.pos - $from.nodeBefore.nodeSize;
                        const end = $from.pos;
                        let tr = state.tr.delete(begin, end);
                        tr = tr.insertText(strings.ZERO_WIDTH_CHAR, begin, begin);
                        dispatch(tr);
                        event.preventDefault();
                        return true;
                    }
                    // 顺序为[···、零宽字符(可能)、customSlot、光标、零宽字符(可能)、 ····] -> [···、光标、····]
                    if (beforeNode && beforeNode.attrs.isCustomSlot) {
                        const parent = $from.parent;
                        const index = $from.index();  // 当前光标在 parent.children 中的 offset
                        const initalStart = $from.pos - beforeNode.nodeSize;
                        const intialEnd = $from.pos;
                        let deleteStart = initalStart;
                        let deleteEnd = intialEnd;
                        if (index > 1) {
                            const prevPrevNode = parent.child(index - 2);
                            if (prevPrevNode && prevPrevNode.isText && prevPrevNode.text.endsWith(strings.ZERO_WIDTH_CHAR)) {
                                deleteStart = deleteStart - 1;
                            }
                        }
                        if (afterNode.isText && afterNode.text.startsWith(strings.ZERO_WIDTH_CHAR)) {
                            deleteEnd = deleteEnd + 1;
                        }
                        if (deleteStart !== initalStart || deleteEnd !== intialEnd) {
                            const tr = state.tr.delete(deleteStart, deleteEnd);
                            dispatch(tr);
                            event.preventDefault();
                            return true;
                        }
                    }

                    if (afterNode && afterNode.isText && afterNode.text === strings.ZERO_WIDTH_CHAR) {
                        const index = $from.index();  // 当前光标在 parent.children 中的 offset
                        if (index === 0 && $from.pos !== 1) {
                            /**
                             * 判断条件: 节点顺序为[····、前一个 Paragraph、换行、光标、零宽字符、····],按下 delete
                             * 结果: [前一个 Paragraph、光标 ····]
                             */
                            const startPos = selection.from - 2;
                            const tr = state.tr.delete(startPos, selection.to + 1);
                            dispatch(tr);
                            event.preventDefault();
                            return true;
                        }
                    }
                    if (beforeNode && beforeNode.isText && beforeNode.text === strings.ZERO_WIDTH_CHAR) {
                        const parent = $from.parent;
                        const index = $from.index();  // 当前光标在 parent.children 中的 offset
                        if (index > 1) {
                            /** 判断条件: 节点顺序为[···、customSlot、零宽字符、光标、····] 按下 Backspace
                             *  结果： 节点顺序为[···、光标、····]
                             */
                            const prevPrevNode = parent.child(index - 2);
                            if (prevPrevNode.attrs.isCustomSlot) {
                                const deleteStart = $from.pos - beforeNode.nodeSize - prevPrevNode.nodeSize;
                                const tr = state.tr.delete(deleteStart, $from.pos);
                                dispatch(tr);
                                event.preventDefault();
                                return true;
                            }
                            // prevPrevNode 就是你想要的光标前一个节点的前一个节点
                        } else if (index === 1 && node.type.name !== 'inputSlot') {
                            /**
                             * 判断条件：节点顺序 [···、上一个paragraph、换行、零宽字符、光标、customSlot、····]， 按下 Backspace
                             * 结果：[···、原来的上一个paragraph、光标、customSlot、····]
                             */
                            if ($from.pos !== 1) {
                                const startPos = selection.from - 1 - 2;
                                const tr = state.tr.delete(startPos, selection.to);
                                dispatch(tr);
                                event.preventDefault();
                                return true;
                            }
                        }
                    } else {
                        /**
                         * 判断条件：节点顺序为[···、inputSlot、····], 光标在 inputSlot 的首位，按下 backSpace
                         * 结论：1. 如果 inputSlot 前面是零宽字符，则直接将光标移动到零宽字符之前
                         * 2. 如果前面不是零宽字符，则在 inputSlot 前面添加零宽字符，并将光标移动到零宽字符前
                         *    用于解决光标在 inputSlot 前，按下 backSpace，出现 inputSlot 前的内容被删除问题
                         */
                        if (node.type.name === 'inputSlot' && $from.pos === $from.start()) {
                            // 1. 如果前面是零宽字符，则直接将光标移动到零宽字符之前
                            const grandParent = $from.node($from.depth - 1);
                            let parentPrevNode = null;
                            const parentIndex = $from.index($from.depth - 1);
                            if (parentIndex > 0) {
                                parentPrevNode = grandParent.child(parentIndex - 1);
                                if (parentPrevNode && parentPrevNode.isText && parentPrevNode.text.endsWith(strings.ZERO_WIDTH_CHAR)) {
                                    const pos = $from.pos - 2;
                                    dispatch(state.tr.setSelection(TextSelection.create(state.doc, pos)));
                                    event.preventDefault();
                                    return true;
                                }
                            }
                            // 2. 如果前面不是零宽字符，则插入一个零宽字符，并将光标移动到零宽字符之前
                            const pos = $from.pos - 1;
                            let tr = state.tr.insertText(strings.ZERO_WIDTH_CHAR, pos, pos + 1);
                            tr = tr.setSelection(TextSelection.create(tr.doc, pos));
                            dispatch(tr);
                            event.preventDefault();
                            return true;
                        }
                    }
                }

                if (event.key === 'Backspace' && !selection.empty) {
                    let startPos = selection.from;
                    let endPos = selection.to; 
                    const nodeBefore = $from.nodeBefore;
                    const nodeAfter = $from.nodeAfter;
                    if (nodeBefore && nodeBefore.isText && nodeBefore.text.endsWith(strings.ZERO_WIDTH_CHAR)) {
                        startPos -= 1;
                    }
                    if (nodeAfter && nodeAfter.isText && nodeAfter.text.startsWith(strings.ZERO_WIDTH_CHAR)) {
                        endPos += 1;
                    }
                    if (startPos !== selection.from || endPos !== selection.to) {
                        let tr = state.tr.delete(startPos, endPos);
                        dispatch(tr);
                        event.preventDefault();
                        return true;
                    }
                }

                // 光标在 inputSlot 的内部
                if (node.type.name === 'inputSlot') {
                    // 处理当显示 placeholder 时候，按键的光标移动，保证通过一次按键，光标就跳出节点
                    // When the placeholder is displayed, the cursor of the button moves to ensure that the cursor 
                    // jumps out of the node after pressing the button once.
                    const slotVisibleText = node.textContent.replace(new RegExp(strings.ZERO_WIDTH_CHAR, 'g'), '');
                    if (slotVisibleText.length === 0 && node.textContent.length > 0 &&
                        (event.key === 'ArrowLeft' || event.key === 'ArrowRight')
                    ) {
                        // 如果光标在节点内，按左右键时直接跳出节点
                        // If the cursor is within a node, press the left and right keys to jump out of the node directly.
                        const pos = event.key === 'ArrowLeft' ? $from.before() : $from.after();
                        // 拿到光标的选区位置
                        if (selection.from - pos !== 1 && selection.from - pos !== -1) {
                            dispatch(state.tr.setSelection(TextSelection.create(state.doc, pos)));
                            event.preventDefault();
                            return true;
                        }
                    }
                    // 删除 input-slot 的最后一个可见字符时，将整个内容替换为零宽字符
                    // When removing the last visible character of input-slot, replace entire content with zero-width character
                    if (event.key === 'Backspace' && selection.empty) {
                        const zeroWidthRegex = new RegExp(strings.ZERO_WIDTH_CHAR, 'g');
                        const visibleText = node.textContent.replace(zeroWidthRegex, '');
                        const onlyZeroWidth = node.textContent.length > 0 && visibleText.length === 0;

                        if ($from.pos === $from.end() && visibleText.length === 1) {
                            const tr = state.tr.insertText(strings.ZERO_WIDTH_CHAR, $from.start(), $from.end());
                            dispatch(tr);
                            event.preventDefault();
                            return true;
                        }

                        if (onlyZeroWidth) {
                            const pos = $from.before();
                            dispatch(state.tr.delete(pos, pos + node.nodeSize));
                            event.preventDefault();
                            return true;
                        }
                    }

                    // 全选 input-slot 节点内容时，点击删除，插入零宽字符
                    // When selecting all input-slot node content, insert zero-width characters
                    if (!selection.empty && $from.parent === node &&
                        selection.from === $from.start() && selection.to >= $from.end() &&
                        (event.key === 'Backspace')
                    ) {
                        const tr = state.tr;
                        if (selection.to > $from.end()) {
                            tr.delete($from.end(), selection.to);
                        }
                        tr.insertText(strings.ZERO_WIDTH_CHAR, $from.start(), $from.end());
                        const pos = $from.start() + 1;
                        tr.setSelection(TextSelection.create(tr.doc, pos));
                        dispatch(tr);
                        event.preventDefault();
                        return true;
                    }
                }
                return false;
            },
        },
    });
}

export function handlePasteLogic(view: EditorView, event: ClipboardEvent) {
    const { state, dispatch } = view;
    const $from = state.selection.$from;
    let tr = state.tr;
    const parentNode = $from.parent;
    if (parentNode.type.name === 'inputSlot') {
        return specialPasteLogicForInputSlot(event, $from, tr, dispatch, state.selection);
    }
    removeZeroWidthChar($from, tr);
    tr.setMeta(strings.DELETABLE, true);
    dispatch(tr);
    return false;
}

/**
 * specialPasteLogicForInputSlot 处理两种情况
 * 1. 如果是 parentNode 是 input-slot，当里面内容仅为零宽字符时候
 * 直接去掉零宽字符会导致 input-slot 消失， 
 * 因此将此行为处理成获取文字部分，将零宽字符替换为文字内容
 * 2. 如果 parentNode 是 input-slot，选中 input-slot 中的所有文字并粘贴
 * 会导致 input-slot 被删除，因此需要处理成使用复制内容替换原有内容的
 * The `specialPasteLogicForInputSlot` function handles two cases.
 * 1. If the parentNode is input-slot, and its content consists of only zero-width characters...
 * Removing zero-width characters directly will cause the input slot to disappear.
 * Therefore, this behavior is processed by retrieving the text portion and replacing the zero-width characters with the text content.
 * 2. If the parentNode is input-slot, select all the text in the input-slot and paste it.
 * This will cause the input slot to be deleted, so it needs to be handled by replacing the original content with copied content.
 */
export function specialPasteLogicForInputSlot(event: ClipboardEvent, $from: any, tr: Transaction, dispatch: (tr: Transaction) => void, selection: Selection) {
    const parentNode = $from.parent;
    const nodeText = parentNode.textContent;
    const isOnlyZeroWidth = nodeText && nodeText === strings.ZERO_WIDTH_CHAR;
    const isAllTextSelected = !selection.empty && 
        selection.from === $from.start() && selection.to === $from.end();
    if (isOnlyZeroWidth || isAllTextSelected) {
        const pastedText = event.clipboardData?.getData('text/plain') || '';
        if (pastedText) {
            const pos = $from.start();
            tr = tr.insertText(pastedText, pos, pos + nodeText.length); 
            const endPos = pos + pastedText.length;
            tr = tr.setSelection(TextSelection.create(tr.doc, endPos));
            tr.setMeta(strings.DELETABLE, true);
            dispatch(tr);
            event.preventDefault();
            return true;
        }
    }
    return false;
}

export function removeZeroWidthChar($from: any, tr: Transaction) {
    // Handling zero-width characters before and after pasting
    // Check the previous node of the cursor
    if ($from.nodeBefore && $from.nodeBefore.isText && $from.nodeBefore.text === strings.ZERO_WIDTH_CHAR) {
        tr = tr.delete($from.pos - $from.nodeBefore.nodeSize, $from.pos);
        return true;
    }
    // Check the node after the cursor
    if ($from.nodeAfter && $from.nodeAfter.isText && $from.nodeAfter.text === strings.ZERO_WIDTH_CHAR) {
        tr = tr.delete($from.pos, $from.pos + $from.nodeAfter.nodeSize);
        return true;
    }
    return false;
}

export function handleCompositionEndLogic(view: EditorView) {
    const { state, dispatch } = view;
    const $from = state.selection.$from;
    const node = $from.node();
    if (node.type.name === 'inputSlot') {
        const text = node.textContent;
        const zeroWidthRegex = new RegExp(strings.ZERO_WIDTH_CHAR, 'g');
        const cleanText = text.replace(zeroWidthRegex, '');
        if (cleanText.length > 0 && cleanText.length < text.length) {
            const tr = state.tr;
            tr.insertText(cleanText, $from.start(), $from.end());
            dispatch(tr);
            return;
        }
    }
    const tr = state.tr;
    if ($from.nodeBefore && $from.nodeBefore.isText) {
        const text = $from.nodeBefore.text;
        if (text?.startsWith(strings.ZERO_WIDTH_CHAR)) {
            const removeStart = $from.pos - $from.nodeBefore.nodeSize;
            const removeEnd = removeStart + 1;
            tr.delete(removeStart, removeEnd);
            dispatch(tr);
        }
    }
}

export function handleTextInputLogic(view: EditorView, from: number, to: number, text: string) {
    const { state, dispatch } = view;
    const $from = state.selection.$from;
    let tr = state.tr;
    let modified = removeZeroWidthChar($from, tr);

    // Remove zero-width characters before inserting text
    if (modified) {
        tr = tr.insertText(text, tr.selection.from, tr.selection.to);
        dispatch(tr);
        return true; // prevent default
    }
    return false; // continue default behavior
}