import { jsx as _jsx } from "react/jsx-runtime";
import { Popup } from '@gravity-ui/uikit';
import { keydownHandler } from 'prosemirror-keymap';
import { Plugin, TextSelection } from 'prosemirror-state';
// @ts-ignore // TODO: fix cjs build
import { findChildrenByMark, findParentNode } from 'prosemirror-utils';
import { Decoration, DecorationSet } from 'prosemirror-view';
import { ErrorLoggerBoundary } from "../../../../../react-utils/ErrorBoundary.js";
import { findMark, isMarkActive } from "../../../../../utils/marks.js";
import { getReactRendererFromState } from "../../../../behavior/ReactRenderer/index.js";
import { LinkAttr, linkType, normalizeUrlFactory } from "../../../index.js";
import { LinkForm } from "./TooltipView.js";
const className = 'pm-link-focus-deco';
const placement = ['bottom-start', 'bottom-end', 'top-start', 'top-end'];
function getTextNode(state) {
    if (!(state.selection instanceof TextSelection) ||
        !state.selection.empty ||
        !isMarkActive(state, linkType(state.schema)))
        return null;
    const parent = findParentNode(Boolean)(state.selection);
    if (!parent) {
        return null;
    }
    const textNodes = findChildrenByMark(parent?.node, linkType(state.schema));
    const textNode = textNodes.find((n) => {
        const start = n.pos + parent.pos;
        const end = start + n.node.nodeSize;
        return start <= state.selection.from && end >= state.selection.from;
    });
    if (!textNode)
        return null;
    const absolutePos = textNode.pos + parent.pos + 1;
    return {
        ...textNode,
        absolutePos,
        parent,
        from: absolutePos,
        to: absolutePos + textNode.node.nodeSize,
    };
}
export const linkTooltipPlugin = (deps) => {
    let viewTooltip = null;
    return new Plugin({
        view(view) {
            viewTooltip = new SelectionTooltip(view, deps);
            return viewTooltip;
        },
        props: {
            handleKeyDown: keydownHandler({
                Escape: () => viewTooltip?.onEscapeDown() ?? false,
            }),
            decorations(state) {
                const textNode = getTextNode(state);
                if (!textNode)
                    return DecorationSet.empty;
                const { from, to } = textNode;
                return DecorationSet.create(state.doc, [
                    Decoration.inline(from, to, {
                        class: `pm-node-selected ${className}`,
                    }),
                ]);
            },
        },
    });
};
class SelectionTooltip {
    normalizeUrl;
    view;
    textNode;
    textNodeRef;
    isTooltipOpen = false;
    manualHidden = false;
    renderItem;
    selectionTooltipProps = { show: false };
    constructor(view, deps) {
        this.normalizeUrl = normalizeUrlFactory(deps);
        this.view = view;
        this.update(view, null);
    }
    update(view, prevState) {
        if (!view.dom.parentNode) {
            this.hideTooltip();
            return;
        }
        const { state } = view;
        if (prevState && prevState.doc.eq(state.doc) && prevState.selection.eq(state.selection))
            return;
        const prevTextNode = this.textNode;
        this.textNode = getTextNode(view.state);
        const prevRef = this.textNodeRef;
        this.updateTextNodeRef();
        if (!this.textNode || !this.textNodeRef) {
            this.removePlaceholderLink(prevTextNode);
            this.hideTooltip();
            return;
        }
        if (prevRef !== this.textNodeRef) {
            this.manualHidden = false;
        }
        if (this.manualHidden) {
            this.hideTooltip();
        }
        else {
            this.renderTooltip({
                show: true,
                domElem: this.textNodeRef,
                onCancel: () => this.cancelPopup(),
                attrs: this.getMarkAttrs(),
                onChange: this.changeAttrs.bind(this),
                onOpenChange: this.onOpenChange,
            });
        }
    }
    destroy() {
        this.isTooltipOpen = false;
        this.selectionTooltipProps = { show: false };
        this.renderItem?.remove();
        this.renderItem = undefined;
    }
    onEscapeDown() {
        if (this.isTooltipOpen) {
            this.removePlaceholderLink(this.textNode);
            this.manualHidden = true;
            this.hideTooltip();
            return true;
        }
        return false;
    }
    updateTextNodeRef() {
        const decoElem = this.view.dom.getElementsByClassName(className)[0];
        this.textNodeRef = decoElem;
    }
    getMarkAttrs() {
        const { textNode } = this;
        const linkMark = textNode && findMark(textNode.node, linkType(this.view.state.schema));
        return linkMark?.attrs;
    }
    hideTooltip() {
        this.renderTooltip({ show: false });
    }
    renderTooltip(props) {
        this.isTooltipOpen = props.show;
        this.selectionTooltipProps = props;
        this.renderItem = this.renderItem ?? this.createRenderItem();
        this.renderItem.rerender();
    }
    onOpenChange = (open, _e, reason) => {
        if (open)
            return;
        if (reason === 'escape-key') {
            this.cancelPopup();
        }
        else {
            this.onOutisdeClick();
        }
    };
    onOutisdeClick = () => {
        // after all updates of the editor state
        setTimeout(() => {
            if (!this.view.hasFocus()) {
                this.removePlaceholderLink(this.textNode);
                this.hideTooltip();
                this.manualHidden = false;
            }
        });
    };
    cancelPopup() {
        this.removePlaceholderLink(this.textNode, { returnSelection: true });
        this.manualHidden = true;
        this.hideTooltip();
        this.view.focus();
    }
    removePlaceholderLink(textNode, opts) {
        if (textNode) {
            const { view } = this;
            const { state } = view;
            const attrs = findMark(textNode.node, linkType(state.schema))?.attrs;
            if (attrs?.[LinkAttr.IsPlaceholder]) {
                const { from, to } = textNode;
                let tr = state.tr.removeMark(from, to, linkType(state.schema));
                if (opts?.returnSelection) {
                    tr = tr.setSelection(TextSelection.create(tr.doc, from, to));
                }
                view.dispatch(tr);
            }
        }
    }
    changeAttrs({ href }) {
        const { view, textNode } = this;
        if (!textNode)
            return;
        const normalizeResult = this.normalizeUrl(href);
        if (normalizeResult) {
            const { url } = normalizeResult;
            const { from, to } = textNode;
            view.dispatch(view.state.tr.addMark(from, to, linkType(view.state.schema).create({ href: url })));
            setTimeout(this.cancelPopup.bind(this));
        }
    }
    createRenderItem() {
        return getReactRendererFromState(this.view.state).createItem('link-tooltip', () => (_jsx(ErrorLoggerBoundary, { children: _jsx(SelectionTooltipView, { ...this.selectionTooltipProps }) })));
    }
}
const SelectionTooltipView = (props) => {
    const { show } = props;
    if (!show)
        return null;
    const { domElem, onChange, onCancel, onOpenChange, attrs = {} } = props;
    const href = attrs[LinkAttr.Href];
    const autoFocus = attrs[LinkAttr.IsPlaceholder];
    return (_jsx(Popup, { open: true, anchorElement: domElem, placement: placement, onOpenChange: onOpenChange, children: _jsx(LinkForm, { href: href ?? '', autoFocus: autoFocus, onChange: onChange, onCancel: onCancel }) }, href));
};
//# sourceMappingURL=index.js.map
