import { assertExists } from '@blockeditor/global/utils';
import { INLINE_ROOT_ATTR, } from '@blockeditor/inline';
import { BLOCK_ID_ATTR } from '../../consts.js';
import { FORMAT_BLOCK_SUPPORT_FLAVOURS, FORMAT_NATIVE_SUPPORT_FLAVOURS, FORMAT_TEXT_SUPPORT_FLAVOURS, } from './consts.js';
function getCombinedFormatFromInlineEditors(inlineEditors) {
    const formatArr = [];
    inlineEditors.forEach(([inlineEditor, inlineRange]) => {
        if (!inlineRange)
            return;
        const format = inlineEditor.getFormat(inlineRange);
        formatArr.push(format);
    });
    if (formatArr.length === 0)
        return {};
    // format will be active only when all inline editors have the same format.
    return formatArr.reduce((acc, cur) => {
        const newFormat = {};
        for (const key in acc) {
            const typedKey = key;
            if (acc[typedKey] === cur[typedKey]) {
                // This cast is secure because we have checked that the value of the key is the same.
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                newFormat[typedKey] = acc[typedKey];
            }
        }
        return newFormat;
    });
}
function getCombinedFormat(host) {
    let result = {};
    host.std.command
        .pipe()
        .withHost()
        .try(chain => [
        // text selection, corresponding to `formatText` command
        chain
            .getTextSelection()
            .getSelectedBlocks({
            types: ['text'],
            filter: el => FORMAT_TEXT_SUPPORT_FLAVOURS.includes(el.model.flavour),
        })
            .inline((ctx, next) => {
            const { selectedBlocks } = ctx;
            assertExists(selectedBlocks);
            const selectedInlineEditors = selectedBlocks.flatMap(el => {
                const inlineRoot = el.querySelector(`[${INLINE_ROOT_ATTR}]`);
                if (inlineRoot && inlineRoot.inlineEditor.getInlineRange()) {
                    return inlineRoot.inlineEditor;
                }
                return [];
            });
            result = getCombinedFormatFromInlineEditors(selectedInlineEditors.map(e => [e, e.getInlineRange()]));
            next();
        }),
        // block selection, corresponding to `formatBlock` command
        chain
            .getBlockSelections()
            .getSelectedBlocks({
            types: ['block'],
            filter: el => FORMAT_BLOCK_SUPPORT_FLAVOURS.includes(el.model.flavour),
        })
            .inline((ctx, next) => {
            const { selectedBlocks } = ctx;
            assertExists(selectedBlocks);
            const selectedInlineEditors = selectedBlocks.flatMap(el => {
                const inlineRoot = el.querySelector(`[${INLINE_ROOT_ATTR}]`);
                if (inlineRoot) {
                    return inlineRoot.inlineEditor;
                }
                return [];
            });
            result = getCombinedFormatFromInlineEditors(selectedInlineEditors.map(e => [
                e,
                {
                    index: 0,
                    length: e.yTextLength,
                },
            ]));
            next();
        }),
        // native selection, corresponding to `formatNative` command
        chain.inline(() => {
            const selectedInlineEditors = Array.from(host.querySelectorAll(`[${INLINE_ROOT_ATTR}]`))
                .filter(el => {
                const selection = document.getSelection();
                if (!selection || selection.rangeCount === 0)
                    return false;
                const range = selection.getRangeAt(0);
                return range.intersectsNode(el);
            })
                .filter(el => {
                const blockElement = el.closest(`[${BLOCK_ID_ATTR}]`);
                if (blockElement) {
                    return FORMAT_NATIVE_SUPPORT_FLAVOURS.includes(blockElement.model.flavour);
                }
                return false;
            })
                .map(el => el.inlineEditor);
            result = getCombinedFormatFromInlineEditors(selectedInlineEditors.map(e => [e, e.getInlineRange()]));
        }),
    ])
        .run();
    return result;
}
export function handleCommonStyle(host, key) {
    const active = commonActiveWhen(host, key);
    const payload = {
        styles: {
            [key]: active ? null : true,
        },
    };
    host.std.command
        .pipe()
        .withHost()
        .try(chain => [
        chain.getTextSelection().formatText(payload),
        chain.getBlockSelections().formatBlock(payload),
        chain.formatNative(payload),
    ])
        .run();
}
export function commonActiveWhen(host, key) {
    const format = getCombinedFormat(host);
    return key in format;
}
export function isFormatSupported(host) {
    let result = false;
    host.std.command
        .pipe()
        .withHost()
        .try(chain => [
        // text selection, corresponding to `formatText` command
        chain
            .getTextSelection()
            .getSelectedBlocks({
            types: ['text'],
            filter: el => FORMAT_TEXT_SUPPORT_FLAVOURS.includes(el.model.flavour),
        })
            .inline((ctx, next) => {
            const { currentTextSelection, selectedBlocks } = ctx;
            assertExists(currentTextSelection);
            assertExists(selectedBlocks);
            if (currentTextSelection.isCollapsed())
                return;
            const selectedInlineEditors = selectedBlocks.flatMap(el => {
                const inlineRoot = el.querySelector(`[${INLINE_ROOT_ATTR}]`);
                if (inlineRoot && inlineRoot.inlineEditor.getInlineRange()) {
                    return inlineRoot.inlineEditor;
                }
                return [];
            });
            result = selectedInlineEditors.length > 0;
            next();
        }),
        // block selection, corresponding to `formatBlock` command
        chain
            .getBlockSelections()
            .getSelectedBlocks({
            types: ['block'],
            filter: el => FORMAT_BLOCK_SUPPORT_FLAVOURS.includes(el.model.flavour),
        })
            .inline((ctx, next) => {
            const { selectedBlocks } = ctx;
            assertExists(selectedBlocks);
            const selectedInlineEditors = selectedBlocks.flatMap(el => {
                const inlineRoot = el.querySelector(`[${INLINE_ROOT_ATTR}]`);
                if (inlineRoot) {
                    return inlineRoot.inlineEditor;
                }
                return [];
            });
            result = selectedInlineEditors.length > 0;
            next();
        }),
        // native selection, corresponding to `formatNative` command
        chain.inline(() => {
            const selectedInlineEditors = Array.from(host.querySelectorAll(`[${INLINE_ROOT_ATTR}]`))
                .filter(el => {
                const selection = document.getSelection();
                if (!selection || selection.rangeCount === 0)
                    return false;
                const range = selection.getRangeAt(0);
                return range.intersectsNode(el);
            })
                .filter(el => {
                const blockElement = el.closest(`[${BLOCK_ID_ATTR}]`);
                if (blockElement) {
                    return FORMAT_NATIVE_SUPPORT_FLAVOURS.includes(blockElement.model.flavour);
                }
                return false;
            })
                .map(el => el.inlineEditor);
            result = selectedInlineEditors.length > 0;
        }),
    ])
        .run();
    return result;
}
//# sourceMappingURL=utils.js.map