import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useMemo, useRef, useState } from 'react';
import { getStyles } from '@diplodoc/html-extension';
import { Ellipsis as DotsIcon, Eye } from '@gravity-ui/icons';
import { Button, Icon, Label, Menu, Popup } from '@gravity-ui/uikit';
import { cn } from "../../../../classname.js";
import { SharedStateKey } from "../../../behavior/SharedState/index.js";
import { TextAreaFixed as TextArea } from "../../../../forms/TextInput.js";
import { i18n } from "../../../../i18n/common/index.js";
import { debounce } from "../../../../lodash.js";
import { useBooleanState, useElementState } from "../../../../react-utils/hooks.js";
import { useSharedEditingState } from "../../../../react-utils/useSharedEditingState.js";
import { removeNode } from "../../../../utils/remove-node.js";
import { YfmHtmlBlockConsts } from "../YfmHtmlBlockSpecs/const.js";
import "./YfmHtmlBlock.css";
export const cnYfmHtmlBlock = cn('yfm-html-block');
export const STOP_EVENT_CLASSNAME = 'prosemirror-stop-event';
const b = cnYfmHtmlBlock;
export function generateID() {
    return Math.random().toString(36).substr(2, 8);
}
const DEFAULT_PADDING = 20;
const DEFAULT_DELAY = 100;
const createLinkCLickHandler = (value, document) => (event) => {
    event.preventDefault();
    const targetId = value.getAttribute('href');
    if (targetId) {
        const targetElement = document.querySelector(targetId);
        if (targetElement) {
            targetElement.scrollIntoView({ behavior: 'smooth' });
        }
    }
};
const YfmHtmlBlockPreview = ({ html, onClick, config }) => {
    const ref = useRef(null);
    const styles = useRef({});
    const classNames = useRef([]);
    const resizeConfig = useRef({});
    const [height, setHeight] = useState('100%');
    useEffect(() => {
        setStyles(config?.styles);
        setClassNames(config?.classNames);
    }, [config, ref.current?.contentWindow?.document?.body]);
    const handleLoadIFrame = () => {
        const contentWindow = ref.current?.contentWindow;
        handleResizeIFrame();
        if (contentWindow) {
            const frameDocument = contentWindow.document;
            frameDocument.addEventListener('dblclick', () => {
                onClick();
            });
        }
    };
    const handleResizeIFrame = () => {
        if (ref.current) {
            const contentWindow = ref.current?.contentWindow;
            if (contentWindow) {
                const body = contentWindow.document.body;
                if (body) {
                    const height = body.scrollHeight +
                        (resizeConfig.current?.padding || DEFAULT_PADDING) +
                        'px';
                    setHeight(height);
                }
            }
        }
    };
    const setClassNames = (newClassNames) => {
        const body = ref.current?.contentWindow?.document.body;
        if (body && newClassNames) {
            const previousClassNames = classNames.current;
            // remove all classes that were in previousClassNames but are not in classNames
            previousClassNames.forEach((className) => {
                if (!newClassNames.includes(className)) {
                    body.classList.remove(className);
                }
            });
            // add classes that are in classNames
            newClassNames.forEach((className) => {
                if (!body.classList.contains(className)) {
                    body.classList.add(className);
                }
            });
            classNames.current = newClassNames;
        }
    };
    const setStyles = (newStyles) => {
        const body = ref.current?.contentWindow?.document.body;
        if (body && newStyles) {
            const previousStyles = styles.current;
            // remove all styles that are in previousStyles but not in styles
            Object.keys(previousStyles).forEach((property) => {
                if (!Object.prototype.hasOwnProperty.call(newStyles, property)) {
                    body.style.removeProperty(property);
                }
            });
            // sdd or update styles that are in styles
            Object.keys(newStyles).forEach((property) => {
                body.style.setProperty(property, newStyles[property]);
            });
            // update current styles to the new styles
            styles.current = newStyles;
        }
    };
    // finds all relative links (href^="#") and changes their click behavior
    const createAnchorLinkHandlers = (type) => () => {
        const document = ref.current?.contentWindow.document;
        if (document) {
            document.querySelectorAll('a[href^="#"]').forEach((value) => {
                const handler = createLinkCLickHandler(value, document);
                if (type === 'add') {
                    value.addEventListener('click', handler);
                }
                else {
                    value.removeEventListener('click', handler);
                }
            });
        }
    };
    useEffect(() => {
        ref.current?.addEventListener('load', handleLoadIFrame);
        ref.current?.addEventListener('load', createAnchorLinkHandlers('add'));
        return () => {
            ref.current?.removeEventListener('load', handleLoadIFrame);
            ref.current?.removeEventListener('load', createAnchorLinkHandlers('remove'));
        };
    }, [html]);
    useEffect(() => {
        if (ref.current) {
            const resizeObserver = new window.ResizeObserver(debounce(handleResizeIFrame, DEFAULT_DELAY));
            resizeObserver.observe(ref.current);
        }
    }, [ref.current?.contentWindow?.document?.body]);
    return (_jsx("iframe", { style: {
            height,
        }, ref: ref, title: generateID(), frameBorder: 0, className: b('content'), srcDoc: html }));
};
const CodeEditMode = ({ initialText, onSave, onCancel }) => {
    const [text, setText] = useState(initialText || '\n');
    return (_jsx("div", { className: b({ editing: true }), children: _jsxs("div", { className: b('editor'), children: [_jsx(TextArea, { controlProps: {
                        className: STOP_EVENT_CLASSNAME,
                    }, value: text, onUpdate: (v) => {
                        setText(v);
                    }, autoFocus: true }), _jsx("div", { className: b('controls'), children: _jsxs("div", { children: [_jsx(Button, { onClick: onCancel, view: 'flat', children: _jsx("span", { className: STOP_EVENT_CLASSNAME, children: i18n('cancel') }) }), _jsx(Button, { onClick: () => onSave(text), view: 'action', children: _jsx("span", { className: STOP_EVENT_CLASSNAME, children: i18n('save') }) })] }) })] }) }));
};
export const YfmHtmlBlockView = ({ onChange, node, getPos, view, options: { useConfig, sanitize, styles, baseTarget = '_parent', head: headContent = '' }, }) => {
    const entityId = node.attrs[YfmHtmlBlockConsts.NodeAttrs.EntityId];
    const entityKey = useMemo(() => SharedStateKey.define({ name: entityId }), [entityId]);
    const config = useConfig?.();
    const [editing, setEditing, unsetEditing] = useSharedEditingState(view, entityKey);
    const [menuOpen, _openMenu, closeMenu, toggleMenuOpen] = useBooleanState(false);
    const [anchorElement, setAnchorElement] = useElementState();
    if (editing) {
        return (_jsx(CodeEditMode, { initialText: node.attrs[YfmHtmlBlockConsts.NodeAttrs.srcdoc], onCancel: unsetEditing, onSave: (v) => {
                onChange({ [YfmHtmlBlockConsts.NodeAttrs.srcdoc]: v });
                unsetEditing();
            } }));
    }
    let additional = baseTarget ? `<base target="${baseTarget}">` : '';
    if (styles) {
        const stylesContent = typeof styles === 'string'
            ? `<link rel="stylesheet" href="${styles}" />`
            : `<style>${getStyles(styles)}</style>`;
        additional += stylesContent;
    }
    const head = `<head>${headContent || additional}</head>`;
    const body = `<body>${node.attrs[YfmHtmlBlockConsts.NodeAttrs.srcdoc] ?? ''}</body>`;
    const html = `<!DOCTYPE html><html>${head}${body}</html>`;
    const sanitizeFunction = typeof sanitize === 'function' ? sanitize : sanitize?.body;
    const resultHtml = sanitizeFunction ? sanitizeFunction(html) : html;
    return (_jsxs("div", { className: b(), onDoubleClick: setEditing, children: [_jsx(Label, { className: b('label'), icon: _jsx(Icon, { size: 16, data: Eye }), children: i18n('preview') }), _jsx(YfmHtmlBlockPreview, { html: resultHtml, onClick: setEditing, config: config }), _jsxs("div", { className: b('menu'), children: [_jsx(Button, { onClick: toggleMenuOpen, ref: setAnchorElement, size: "s", className: STOP_EVENT_CLASSNAME, children: _jsx(Icon, { data: DotsIcon, className: STOP_EVENT_CLASSNAME }) }), _jsx(Popup, { anchorElement: anchorElement, open: menuOpen, onOpenChange: closeMenu, placement: "bottom-end", children: _jsxs(Menu, { children: [_jsx(Menu.Item, { onClick: () => {
                                        setEditing();
                                        closeMenu();
                                    }, children: i18n('edit') }), _jsx(Menu.Item, { onClick: () => {
                                        const pos = getPos();
                                        if (pos === undefined)
                                            return;
                                        removeNode({
                                            node,
                                            pos,
                                            tr: view.state.tr,
                                            dispatch: view.dispatch,
                                        });
                                    }, children: i18n('remove') })] }) })] })] }));
};
//# sourceMappingURL=YfmHtmlBlockView.js.map
