import { jsx } from "react/jsx-runtime";
import extension_document from "@tiptap/extension-document";
import extension_hard_break from "@tiptap/extension-hard-break";
import extension_mention from "@tiptap/extension-mention";
import extension_paragraph from "@tiptap/extension-paragraph";
import extension_text from "@tiptap/extension-text";
import { Placeholder, UndoRedo } from "@tiptap/extensions";
import { Fragment, Node, Slice } from "@tiptap/pm/model";
import { EditorContent, ReactNodeViewRenderer, useEditor } from "@tiptap/react";
import { exitSuggestion } from "@tiptap/suggestion";
import react, { forwardRef, useCallback } from "react";
import { ResourceChipNodeView } from "./resource-chip-node-view.js";
import { textToDocument } from "./tiptap.utils.js";
import { EditorContainer } from "./tiptap-editor.styles.js";
import { ResourceMentionPluginKey, createResourceSuggestion } from "./tiptap-resource-suggestion.js";
const ResourceMention = extension_mention.extend({
    addAttributes () {
        return {
            ...this.parent?.(),
            type: {
                parseHTML: (element)=>element.getAttribute('data-resource-type'),
                renderHTML: (attributes)=>attributes.type ? {
                        'data-resource-type': attributes.type
                    } : {}
            },
            icon: {
                parseHTML: (element)=>element.getAttribute('data-icon'),
                renderHTML: (attributes)=>attributes.icon ? {
                        'data-icon': attributes.icon
                    } : {}
            }
        };
    },
    addNodeView () {
        return ReactNodeViewRenderer(ResourceChipNodeView);
    }
});
function TipTapEditorInner(props, ref) {
    const { value = '', placeholder = '', minRows = 1, maxRows = 6, lineHeight = '20px', onChange, onKeyDown, onMentionStart, onMentionEnd, onMentionQueryChange } = props;
    const callbacksRef = react.useRef({
        onChange,
        onKeyDown,
        onMentionStart,
        onMentionEnd,
        onMentionQueryChange
    });
    const mentionRangeRef = react.useRef(null);
    const suppressSuggestionRef = react.useRef(false);
    react.useLayoutEffect(()=>{
        callbacksRef.current = {
            onChange,
            onKeyDown,
            onMentionStart,
            onMentionEnd,
            onMentionQueryChange
        };
    }, [
        onChange,
        onKeyDown,
        onMentionStart,
        onMentionEnd,
        onMentionQueryChange
    ]);
    const resourceSuggestion = react.useMemo(()=>createResourceSuggestion({
            onStart: (range, coords)=>{
                mentionRangeRef.current = range;
                callbacksRef.current.onMentionStart?.(range, coords);
            },
            onExit: ()=>{
                mentionRangeRef.current = null;
                callbacksRef.current.onMentionEnd?.();
            },
            onQueryChange: (query, range)=>{
                mentionRangeRef.current = range;
                callbacksRef.current.onMentionQueryChange?.(query, range);
            }
        }, suppressSuggestionRef), []);
    const extensions = react.useMemo(()=>[
            extension_document,
            extension_paragraph,
            extension_text,
            UndoRedo,
            extension_hard_break.configure({
                keepMarks: false
            }),
            Placeholder.configure({
                placeholder,
                emptyEditorClass: 'is-editor-empty'
            }),
            ResourceMention.configure({
                deleteTriggerWithBackspace: true,
                suggestion: resourceSuggestion,
                renderText: ({ node })=>node.attrs.label ?? node.attrs.id
            })
        ], [
        placeholder,
        resourceSuggestion
    ]);
    const editor = useEditor({
        extensions,
        coreExtensionOptions: {
            clipboardTextSerializer: {
                blockSeparator: '\n'
            }
        },
        content: value ? textToDocument(value) : '',
        onUpdate: ({ editor: ed })=>{
            const text = ed.getText();
            if (0 === text.trim().length) ed.commands.clearContent();
            callbacksRef.current.onChange?.(ed.getText());
            ed.commands.scrollIntoView();
        },
        editorProps: {
            handleKeyDown: (_view, event)=>callbacksRef.current.onKeyDown?.(event) ?? false,
            handleDOMEvents: {
                mousedown: ()=>{
                    suppressSuggestionRef.current = false;
                    return false;
                },
                keydown: ()=>{
                    suppressSuggestionRef.current = false;
                    return false;
                }
            },
            attributes: {
                'data-testid': 'tiptap-editor'
            },
            clipboardTextParser: (text, context)=>{
                const doc = textToDocument(text);
                const nodes = doc.content.map((para)=>Node.fromJSON(context.doc.type.schema, para));
                const fragment = Fragment.fromArray(nodes);
                return Slice.maxOpen(fragment);
            }
        }
    });
    react.useEffect(()=>{
        if (!editor) return;
        const currentText = editor.getText();
        if (currentText === value) return;
        editor.commands.setContent(value ? textToDocument(value) : '', {
            emitUpdate: false
        });
    }, [
        value,
        editor
    ]);
    const insertResource = useCallback((resource, range)=>{
        if (!editor) return;
        const chain = editor.chain().focus();
        if (range) chain.deleteRange(range);
        chain.insertContent({
            type: 'mention',
            attrs: {
                id: resource.id,
                label: resource.displayName,
                type: resource.type,
                icon: resource.icon
            }
        }).insertContent({
            type: 'text',
            text: ' '
        }).run();
    }, [
        editor
    ]);
    const getSerializedContent = useCallback(()=>{
        if (!editor) return '';
        try {
            return editor.getText({
                blockSeparator: '\n',
                textSerializers: {
                    mention: ({ node })=>{
                        const { id, label, type, icon } = node.attrs;
                        return `[[resource-token:${JSON.stringify({
                            id,
                            type,
                            displayName: label,
                            ...icon && {
                                icon
                            }
                        })}]]`;
                    },
                    hardBreak: ()=>'\n'
                }
            });
        } catch  {
            return editor.getText() || '';
        }
    }, [
        editor
    ]);
    const focus = useCallback(()=>{
        editor?.commands.focus();
    }, [
        editor
    ]);
    const clear = useCallback(()=>{
        editor?.commands.clearContent();
    }, [
        editor
    ]);
    const clearMentionQuery = useCallback(()=>{
        if (!editor || !mentionRangeRef.current) return;
        const range = mentionRangeRef.current;
        const queryStart = range.from + 1;
        if (queryStart < range.to) {
            editor.chain().focus().deleteRange({
                from: queryStart,
                to: range.to
            }).run();
            mentionRangeRef.current = {
                from: range.from,
                to: queryStart
            };
        }
    }, [
        editor
    ]);
    const triggerMention = useCallback(()=>{
        if (!editor) return;
        suppressSuggestionRef.current = false;
        editor.chain().focus().insertContent('@').run();
    }, [
        editor
    ]);
    const exitMention = useCallback(()=>{
        if (!editor) return;
        suppressSuggestionRef.current = true;
        exitSuggestion(editor.view, ResourceMentionPluginKey);
    }, [
        editor
    ]);
    react.useImperativeHandle(ref, ()=>({
            focus,
            insertResource,
            getSerializedContent,
            clear,
            clearMentionQuery,
            triggerMention,
            exitMention
        }), [
        focus,
        insertResource,
        getSerializedContent,
        clear,
        clearMentionQuery,
        triggerMention,
        exitMention
    ]);
    return /*#__PURE__*/ jsx(EditorContainer, {
        minRows: minRows,
        maxRows: maxRows,
        lineHeight: lineHeight,
        onClick: focus,
        children: /*#__PURE__*/ jsx(EditorContent, {
            editor: editor
        })
    });
}
const TipTapEditor = /*#__PURE__*/ react.memo(/*#__PURE__*/ forwardRef(TipTapEditorInner));
TipTapEditor.displayName = 'TipTapEditor';
export { TipTapEditor };
