import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
import { $isListItemNode, $isListNode, ListNode } from '@lexical/list';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { $isHeadingNode } from '@lexical/rich-text';
import { $getSelectionStyleValueForProperty, getStyleObjectFromCSS } from '@lexical/selection';
import { $findMatchingParent, $getNearestNodeOfType, mergeRegister } from '@lexical/utils';
import { $getSelection, $isElementNode, $isNodeSelection, $isRangeSelection, $isRootOrShadowRoot, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_NORMAL, KEY_MODIFIER_COMMAND, SELECTION_CHANGE_COMMAND, } from 'lexical';
import { useCallback, useEffect, useState } from 'react';
import * as React from 'react';
import { getSelectedNode } from '../../utils/getSelectedNode';
import { sanitizeUrl } from '../../utils/url';
import HeaderDropdown, { DEFAULT_HEADING_FONT_SIZES, getListItemBlockType, getSelectedListItems, } from './components/heading';
import FontSize from './components/font-size';
import BoldText from './components/bold-text';
import ItalicText from './components/italic-text';
import UnderlineText from './components/underline-text';
import TextColor from './components/text-color';
import TextAlign from './components/text-align';
import Bullet from './components/bullet';
import NumberBullet from './components/number-bullet';
import UploadPdf from './components/upload-pdf';
import UploadImage from './components/upload-image';
import Link from './components/link';
import { $isImageNode } from '../../nodes/ImageNode';
import { $isInlineImageNode } from '../../nodes/InlineImageNode';
const blockTypeToBlockName = {
    bullet: 'Bulleted List',
    check: 'Check List',
    code: 'Code Block',
    h1: 'Heading 1',
    h2: 'Heading 2',
    h3: 'Heading 3',
    h4: 'Heading 4',
    h5: 'Heading 5',
    h6: 'Heading 6',
    number: 'Numbered List',
    paragraph: 'Normal',
    quote: 'Quote',
};
/**
 * Checks whether a block type is a heading type.
 * @param value Block type to test.
 * @returns True when the value is a heading block type.
 */
function isHeadingBlockType(value) {
    return value in DEFAULT_HEADING_FONT_SIZES;
}
/**
 * Gets the fallback block type represented by a list item font size.
 * @param fontSize Font size from the selected list item.
 * @returns Matching heading type, or paragraph when there is no match.
 */
function getBlockTypeFromFontSize(fontSize) {
    var _a;
    const headingType = (_a = Object.entries(DEFAULT_HEADING_FONT_SIZES).find(([, size]) => size === fontSize)) === null || _a === void 0 ? void 0 : _a[0];
    return headingType !== undefined ? headingType : 'paragraph';
}
/**
 * Reads font size from a serialized style string.
 * @param style Serialized CSS style string.
 * @returns Font size value when present.
 */
function getFontSizeFromStyle(style) {
    return getStyleObjectFromCSS(style)['font-size'];
}
function DisabledToolbarGroup({ children, disabled, }) {
    return (React.createElement("div", { className: disabled ? 'relative cursor-not-allowed opacity-40' : undefined },
        children,
        disabled && React.createElement("div", { className: 'absolute inset-0 z-10 cursor-not-allowed' })));
}
export default function ToolbarPlugin({ apiCdnUpload, disabled, }) {
    const [editor] = useLexicalComposerContext();
    const [activeEditor, setActiveEditor] = useState(editor);
    const [blockType, setBlockType] = useState('paragraph');
    const [listBlockType, setListBlockType] = useState(null);
    const [fontSize, setFontSize] = useState('14px');
    const [fontColor, setFontColor] = useState('#000');
    const [elementFormat, setElementFormat] = useState('left');
    const [isImageSelected, setIsImageSelected] = useState(false);
    const [isLink, setIsLink] = useState(false);
    const [isBold, setIsBold] = useState(false);
    const [isItalic, setIsItalic] = useState(false);
    const [isUnderline, setIsUnderline] = useState(false);
    const $updateToolbar = useCallback(() => {
        var _a;
        const selection = $getSelection();
        if ($isNodeSelection(selection)) {
            const node = selection.getNodes()[0];
            if ($isImageNode(node) || $isInlineImageNode(node)) {
                setIsImageSelected(true);
                setElementFormat((node.getPosition() || 'center'));
            }
            else {
                setIsImageSelected(false);
            }
            return;
        }
        if ($isRangeSelection(selection)) {
            setIsImageSelected(false);
            const anchorNode = selection.anchor.getNode();
            let element = anchorNode.getKey() === 'root'
                ? anchorNode
                : $findMatchingParent(anchorNode, (e) => {
                    const parent = e.getParent();
                    return parent !== null && $isRootOrShadowRoot(parent);
                });
            if (element === null) {
                element = anchorNode.getTopLevelElementOrThrow();
            }
            const elementKey = element.getKey();
            const elementDOM = activeEditor.getElementByKey(elementKey);
            // Update text format
            setIsBold(selection.hasFormat('bold'));
            setIsItalic(selection.hasFormat('italic'));
            setIsUnderline(selection.hasFormat('underline'));
            // Update links
            const node = getSelectedNode(selection);
            const parent = node.getParent();
            if ($isLinkNode(parent) || $isLinkNode(node)) {
                setIsLink(true);
            }
            else {
                setIsLink(false);
            }
            const defaultFontSize = $isHeadingNode(element) ? DEFAULT_HEADING_FONT_SIZES[element.getTag()] : '14px';
            let selectionFontSize = $getSelectionStyleValueForProperty(selection, 'font-size', defaultFontSize);
            if (elementDOM !== null) {
                const parentList = $getNearestNodeOfType(anchorNode, ListNode);
                if (parentList !== null || $isListNode(element)) {
                    const listItemNode = $isListItemNode(anchorNode)
                        ? anchorNode
                        : $findMatchingParent(anchorNode, (node) => $isListItemNode(node));
                    const selectedListItems = getSelectedListItems(selection);
                    const firstSelectedListItem = (_a = selectedListItems.values().next().value) !== null && _a !== void 0 ? _a : listItemNode;
                    const listItemFontSize = $isListItemNode(firstSelectedListItem)
                        ? getFontSizeFromStyle(firstSelectedListItem.getTextStyle())
                        : undefined;
                    const listItemBlockType = $isListItemNode(firstSelectedListItem)
                        ? getListItemBlockType(firstSelectedListItem)
                        : null;
                    // @ts-ignore
                    const type = parentList ? parentList.getListType() : element.getListType();
                    if (listItemFontSize !== undefined && !selection.isCollapsed()) {
                        selectionFontSize = listItemFontSize;
                    }
                    setListBlockType(type);
                    setBlockType(listItemBlockType !== null && listItemBlockType !== void 0 ? listItemBlockType : getBlockTypeFromFontSize(selectionFontSize));
                }
                else {
                    setListBlockType(null);
                    const type = $isHeadingNode(element) ? element.getTag() : element.getType();
                    if (type in blockTypeToBlockName) {
                        setBlockType(type);
                    }
                }
            }
            // Handle buttons
            setFontSize(selectionFontSize);
            setFontColor($getSelectionStyleValueForProperty(selection, 'color', '#000'));
            let matchingParent;
            if ($isLinkNode(parent)) {
                // If node is a link, we need to fetch the parent paragraph node to set format
                matchingParent = $findMatchingParent(node, (parentNode) => $isElementNode(parentNode) && !parentNode.isInline());
            }
            // If matchingParent is a valid node, pass it's format type
            setElementFormat($isElementNode(matchingParent)
                ? matchingParent.getFormatType()
                : $isElementNode(node)
                    ? node.getFormatType()
                    : (parent === null || parent === void 0 ? void 0 : parent.getFormatType()) || 'left');
        }
    }, [activeEditor]);
    useEffect(() => activeEditor.registerCommand(SELECTION_CHANGE_COMMAND, (_payload, newEditor) => {
        $updateToolbar();
        setActiveEditor(newEditor);
        return false;
    }, COMMAND_PRIORITY_CRITICAL), [activeEditor, $updateToolbar]);
    useEffect(() => mergeRegister(activeEditor.registerUpdateListener(({ editorState }) => {
        editorState.read(() => {
            $updateToolbar();
        });
    })), [$updateToolbar, activeEditor]);
    useEffect(() => activeEditor.registerCommand(KEY_MODIFIER_COMMAND, (payload) => {
        const event = payload;
        const { code, ctrlKey, metaKey } = event;
        if (code === 'KeyK' && (ctrlKey || metaKey)) {
            event.preventDefault();
            return activeEditor.dispatchCommand(TOGGLE_LINK_COMMAND, sanitizeUrl('https://'));
        }
        return false;
    }, COMMAND_PRIORITY_NORMAL), [activeEditor, isLink]);
    useEffect(() => {
        if (!window.pdfjsLib) {
            const script = document.createElement('script');
            script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js';
            document.body.appendChild(script);
        }
    }, []);
    useEffect(() => {
        if (activeEditor) {
            activeEditor.setEditable(!disabled);
        }
    }, [activeEditor, disabled]);
    const parsedFontSize = parseInt(fontSize, 10);
    const activeListHeadingTag = listBlockType !== null && isHeadingBlockType(blockType) ? blockType : null;
    return (React.createElement("div", { className: 'flex relative flex-wrap gap-1 p-2 bg-gray-100 border-b border-gray-300' },
        disabled && React.createElement("div", { className: 'cursor-not-allowed z-20 absolute inset-0 bg-gray-200 opacity-20' }),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected }, blockType in blockTypeToBlockName && activeEditor === editor && (React.createElement(HeaderDropdown, { value: blockType, editor: activeEditor }))),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(FontSize, { editor: activeEditor, fontSize: Number.isNaN(parsedFontSize) ? null : parsedFontSize, listHeadingTag: activeListHeadingTag })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(BoldText, { isActive: isBold, editor: activeEditor })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(ItalicText, { isActive: isItalic, editor: activeEditor })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(UnderlineText, { isActive: isUnderline, editor: activeEditor })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(TextColor, { editor: activeEditor, color: fontColor })),
        React.createElement(TextAlign, { value: elementFormat, editor: activeEditor, isImageSelected: isImageSelected }),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(Bullet, { isActive: listBlockType === 'bullet', editor: activeEditor })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(NumberBullet, { isActive: listBlockType === 'number', editor: activeEditor })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(UploadPdf, { editor: activeEditor, apiCdnUpload: apiCdnUpload })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(UploadImage, { editor: activeEditor, apiCdnUpload: apiCdnUpload })),
        React.createElement(DisabledToolbarGroup, { disabled: isImageSelected },
            React.createElement(Link, { editor: activeEditor }))));
}
//# sourceMappingURL=index.js.map