import { keySpace, keyEnter, keyTab, keyEscape } from '@ni/fast-web-utilities';
import { waitForUpdatesAsync } from '../../../testing/async-helpers';
import { toggleButtonTag } from '../../../toggle-button';
import { ArrowKeyButton, ToolbarButton } from './types';
import { getTagsFromElement, getLeafContentsFromElement, getLastChildElement, getLastChildElementAttribute } from '../../models/testing/markdown-parser-utils';
import { richTextMentionUsersViewTag } from '../../../rich-text-mention/users/view';
import { RichTextMarkdownParser } from '../../models/markdown-parser';
import { buttonTag } from '../../../button';
import { richTextMentionListboxTag } from '../../mention-listbox';
import { listOptionTag } from '../../../list-option';
import { anchoredRegionTag } from '../../../anchored-region';
import { iconAtTag } from '../../../icons/at';
import { MarkdownParserMentionConfiguration } from '../../models/markdown-parser-mention-configuration';
import { MentionInternals } from '../../../rich-text-mention/base/models/mention-internals';
import { MappingUserConfig } from '../../../rich-text-mention/users/models/mapping-user-config';
import { RichTextMentionUsersValidator } from '../../../rich-text-mention/users/models/rich-text-mention-users-validator';
/**
 * Page object for the `nimble-rich-text-editor` component.
 */
export class RichTextEditorPageObject {
    constructor(richTextEditorElement) {
        this.richTextEditorElement = richTextEditorElement;
    }
    editorSectionHasChildNodes() {
        const editorSection = this.getEditorSection();
        return editorSection.hasChildNodes();
    }
    getEditorSectionFirstElementChildClassName() {
        const editorSection = this.getEditorSection();
        return editorSection.firstElementChild.className;
    }
    async clickEditorShortcutKeys(shortcutKey, isShiftKey) {
        const editor = this.getTiptapEditor();
        const event = new KeyboardEvent('keydown', {
            key: shortcutKey,
            ctrlKey: true,
            shiftKey: isShiftKey,
            bubbles: true,
            cancelable: true
        });
        editor.dispatchEvent(event);
        await waitForUpdatesAsync();
    }
    async pressEnterKeyInEditor() {
        const editor = this.getTiptapEditor();
        const event = new KeyboardEvent('keydown', {
            key: keyEnter,
            bubbles: true,
            cancelable: true
        });
        editor.dispatchEvent(event);
        await waitForUpdatesAsync();
    }
    async pressEscapeKeyInEditor() {
        const editor = this.getTiptapEditor();
        const event = new KeyboardEvent('keydown', {
            key: keyEscape,
            bubbles: true,
            cancelable: true
        });
        editor.dispatchEvent(event);
        await waitForUpdatesAsync();
    }
    async pressArrowKeyInEditor(arrowButton) {
        const editor = this.getTiptapEditor();
        const event = new KeyboardEvent('keydown', {
            key: arrowButton,
            bubbles: true,
            cancelable: true
        });
        editor.dispatchEvent(event);
        await waitForUpdatesAsync();
    }
    async pressShiftEnterKeysInEditor() {
        const editor = this.getTiptapEditor();
        const shiftEnterEvent = new KeyboardEvent('keydown', {
            key: keyEnter,
            shiftKey: true,
            bubbles: true,
            cancelable: true
        });
        editor.dispatchEvent(shiftEnterEvent);
        await waitForUpdatesAsync();
    }
    async pressTabKeyInEditor() {
        const editor = this.getTiptapEditor();
        const event = new KeyboardEvent('keydown', {
            key: keyTab,
            bubbles: true,
            cancelable: true
        });
        editor.dispatchEvent(event);
        await waitForUpdatesAsync();
    }
    async pressShiftTabKeysInEditor() {
        const editor = this.getTiptapEditor();
        const shiftTabEvent = new KeyboardEvent('keydown', {
            key: keyTab,
            shiftKey: true,
            bubbles: true,
            cancelable: true
        });
        editor.dispatchEvent(shiftTabEvent);
        await waitForUpdatesAsync();
    }
    /**
     * In testing environment, when clicking on the footer button, it may not persist in the same state if any editor transaction occurs in between.
     * This behavior is likely due to dynamic modifications of formatting button states based on cursor positions during editor transactions.
     * Setting the "force" parameter to true activates the formatting button state; when set to false, it deactivates the state.
     * If unset, the state toggles by interacting with it once.
     */
    async toggleFooterButton(button, force) {
        const toggleButton = this.getFormattingButton(button);
        const event = new Event('mousedown', { bubbles: true });
        toggleButton.dispatchEvent(event);
        toggleButton.click();
        let format = Object.keys(ToolbarButton).find(key => ToolbarButton[key] === button);
        // In the editor, the isActive() method expects the format name to be 'italic' instead of 'italics.'
        // As it was consistently represent it as 'italics' elsewhere just updated it here
        if (format === 'italics') {
            format = 'italic';
        }
        if (force === true || force === false) {
            if (this.richTextEditorElement.tiptapEditor.isActive(format)
                !== force) {
                toggleButton.dispatchEvent(event);
                toggleButton.click();
            }
        }
        await waitForUpdatesAsync();
    }
    async clickUserMentionButton() {
        const userMentionButton = this.getUserMentionButton();
        userMentionButton.click();
        await waitForUpdatesAsync();
    }
    getMentionButtonIcon(buttonIndex) {
        const buttons = this.getMentionButtons();
        return buttons[buttonIndex]?.firstElementChild?.tagName;
    }
    getMentionButtonTitle(buttonIndex) {
        const buttons = this.getMentionButtons();
        return buttons[buttonIndex]?.getAttribute('title') ?? '';
    }
    getMentionButtonLabel(buttonIndex) {
        const buttons = this.getMentionButtons();
        return (buttons[buttonIndex]?.innerText ?? '').trim();
    }
    getButtonCheckedState(button) {
        const toggleButton = this.getFormattingButton(button);
        return toggleButton.checked;
    }
    getButtonTabIndex(button) {
        const toggleButton = this.getFormattingButton(button);
        return toggleButton.tabIndex;
    }
    spaceKeyActivatesButton(button) {
        const toggleButton = this.getFormattingButton(button);
        const event = new KeyboardEvent('keypress', {
            key: keySpace
        });
        toggleButton.control.dispatchEvent(event);
    }
    enterKeyActivatesButton(button) {
        const toggleButton = this.getFormattingButton(button);
        const event = new KeyboardEvent('keypress', {
            key: keyEnter
        });
        toggleButton.control.dispatchEvent(event);
    }
    async pasteToEditor(text) {
        const editor = this.getTiptapEditor();
        const pasteEvent = new ClipboardEvent('paste', {
            clipboardData: new DataTransfer()
        });
        pasteEvent.clipboardData?.setData('text/plain', text);
        editor.dispatchEvent(pasteEvent);
        await this.focusEditorIfMentionListboxOpened();
    }
    // Simulate the actual pasting of content by passing the extracted HTML string as an argument and setting the format to 'text/html',
    // as in the [DataFormat](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object.
    // For example, when copying a link, the clipboard stores information that includes the anchor tag, href attribute value etc, and paste it as an HTML string
    async pasteHTMLToEditor(htmlString) {
        const editor = this.getTiptapEditor();
        const pasteEvent = new ClipboardEvent('paste', {
            clipboardData: new DataTransfer()
        });
        pasteEvent.clipboardData?.setData('text/html', htmlString);
        editor.dispatchEvent(pasteEvent);
        await this.focusEditorIfMentionListboxOpened();
    }
    async setEditorTextContent(value) {
        this.richTextEditorElement.tiptapEditor
            .chain()
            .focus()
            .insertContent({
            type: 'text',
            text: value
        })
            .run();
        await waitForUpdatesAsync();
        await this.focusEditorIfMentionListboxOpened();
    }
    async setCursorPosition(position) {
        this.richTextEditorElement.tiptapEditor.commands.setTextSelection(position);
        await waitForUpdatesAsync();
    }
    async replaceEditorContent(value) {
        const lastElement = this.getEditorLastChildElement();
        lastElement.parentElement.textContent = value;
        await waitForUpdatesAsync();
        await this.focusEditorIfMentionListboxOpened();
    }
    async sliceEditorContent(from, to) {
        this.richTextEditorElement.tiptapEditor
            .chain()
            .focus()
            .deleteRange({ from, to })
            .run();
        await waitForUpdatesAsync();
        await this.focusEditorIfMentionListboxOpened();
    }
    getEditorLastChildAttribute(attribute) {
        return getLastChildElementAttribute(attribute, this.getTiptapEditor());
    }
    async isMentionListboxOpened() {
        await waitForUpdatesAsync();
        return !this.getMentionListbox()?.shadowRoot?.querySelector(anchoredRegionTag)?.hidden;
    }
    getEditorMentionViewAttributeValues(attribute) {
        return Array.from(this.getTiptapEditor().querySelectorAll(richTextMentionUsersViewTag)).map(el => el.getAttribute(attribute) || '');
    }
    getEditorFirstChildTagName() {
        return this.getTiptapEditor().firstElementChild?.tagName ?? '';
    }
    getEditorFirstChildTextContent() {
        return this.getTiptapEditor().firstElementChild?.textContent ?? '';
    }
    getEditorTextContents() {
        return Array.from(this.getTiptapEditor().querySelectorAll('*')).map(el => el.textContent || '');
    }
    getEditorTagNames() {
        return getTagsFromElement(this.getTiptapEditor());
    }
    // Return list of tags, excluding those such as 'IMG' (prosemirror-separator) that do not affect the UI or markdown output.
    // These tags are considered extraneous and are added by prosemirror.
    getMarkdownRenderedTagNames() {
        return getTagsFromElement(this.getTiptapEditor()).filter(tag => tag !== 'IMG');
    }
    getEditorLeafContents() {
        return getLeafContentsFromElement(this.getTiptapEditor());
    }
    getEditorTagNamesWithClosingTags() {
        const tagNames = [];
        const tiptapEditor = this.getTiptapEditor();
        const processNode = (node) => {
            if (node.nodeType === Node.ELEMENT_NODE) {
                const el = node;
                tagNames.push(el.tagName);
                el.childNodes.forEach(processNode);
                tagNames.push(`/${el.tagName}`);
            }
        };
        processNode(tiptapEditor);
        return tagNames.slice(1, -1);
    }
    getFormattingButtonTextContent(toolbarButton) {
        const button = this.getFormattingButton(toolbarButton);
        return button.textContent.trim();
    }
    getFormattingButtonTitle(toolbarButton) {
        const button = this.getFormattingButton(toolbarButton);
        return button.title;
    }
    isRichTextEditorActiveElement() {
        return (document.activeElement === this.richTextEditorElement
            && document.activeElement?.shadowRoot?.activeElement
                === this.getTiptapEditor());
    }
    getEditorTabIndex() {
        return this.getTiptapEditor().getAttribute('tabindex') ?? '';
    }
    async setFooterHidden(footerHidden) {
        if (footerHidden) {
            this.richTextEditorElement.setAttribute('footer-hidden', '');
        }
        else {
            this.richTextEditorElement.removeAttribute('footer-hidden');
        }
        await waitForUpdatesAsync();
    }
    isFooterHidden() {
        const footerSection = this.getFooter();
        return window.getComputedStyle(footerSection).display === 'none';
    }
    async setDisabled(disabled) {
        if (disabled) {
            this.richTextEditorElement.setAttribute('disabled', '');
        }
        else {
            this.richTextEditorElement.removeAttribute('disabled');
        }
        await waitForUpdatesAsync();
    }
    isButtonDisabled(button) {
        const toggleButton = this.getFormattingButton(button);
        return toggleButton.hasAttribute('disabled');
    }
    getPlaceholderValue() {
        const editor = this.getTiptapEditor();
        return editor.firstElementChild?.getAttribute('data-placeholder') ?? '';
    }
    getParsedHtmlFromMarkdown(markdown, mappings) {
        const parserMentionConfigForUser = this.getParserMentionConfigForUser(mappings);
        const parseResult = RichTextMarkdownParser.parseMarkdownToDOM(markdown, [parserMentionConfigForUser]);
        return this.richTextEditorElement.xmlSerializer.serializeToString(parseResult.fragment);
    }
    async focusOutEditor() {
        const focusout = new FocusEvent('focusout');
        this.richTextEditorElement.dispatchEvent(focusout);
        await waitForUpdatesAsync();
    }
    getMentionListboxItemsName() {
        const listItemsName = [];
        this.getAllListItemsInMentionBox().forEach(item => (item.hidden ? null : listItemsName.push(item.textContent)));
        return listItemsName;
    }
    getSelectedOption() {
        const nodeList = this.getAllListItemsInMentionBox();
        return (Array.from(nodeList).find(item => item.selected && !item.hasAttribute('hidden'))?.textContent ?? '');
    }
    async clickMentionListboxOption(index) {
        await this.focusEditorIfMentionListboxOpened();
        const listOption = this.getAllListItemsInMentionBox()[index];
        listOption?.click();
        await waitForUpdatesAsync();
    }
    moveCursorToStart() {
        this.richTextEditorElement.tiptapEditor.commands.focus('start');
    }
    getCursorPosition() {
        return this.richTextEditorElement.tiptapEditor.state.selection.anchor;
    }
    getEditorSection() {
        return this.richTextEditorElement.shadowRoot?.querySelector('.editor');
    }
    getFooter() {
        return this.richTextEditorElement.shadowRoot.querySelector('.footer-section');
    }
    getTiptapEditor() {
        return this.richTextEditorElement.shadowRoot.querySelector('.ProseMirror');
    }
    getFormattingButton(button) {
        const buttons = this.richTextEditorElement.shadowRoot.querySelectorAll(toggleButtonTag);
        return buttons[button];
    }
    getUserMentionButton() {
        const buttons = this.getMentionButtons();
        return buttons.find(button => button.querySelector(iconAtTag));
    }
    getMentionButtons() {
        return Array.from(this.richTextEditorElement.shadowRoot.querySelectorAll(buttonTag));
    }
    getMentionListbox() {
        return this.richTextEditorElement.shadowRoot.querySelector(richTextMentionListboxTag);
    }
    getAllListItemsInMentionBox() {
        return this.getMentionListbox().querySelectorAll(listOptionTag);
    }
    getEditorLastChildElement() {
        return getLastChildElement(this.getTiptapEditor());
    }
    getParserMentionConfigForUser(mappings = []) {
        const mentionInternals = new MentionInternals({
            character: '',
            icon: '',
            viewElement: richTextMentionUsersViewTag,
            validator: new RichTextMentionUsersValidator()
        }, () => { });
        mentionInternals.pattern = '^user:(.*)';
        mappings.forEach(mapping => {
            const mappingConfig = new MappingUserConfig(mapping.key, mapping.displayName);
            mentionInternals.mappingConfigs = new Map().set(mapping.key, mappingConfig);
        });
        const parserMentionConfig = new MarkdownParserMentionConfiguration(mentionInternals);
        return parserMentionConfig;
    }
    async focusEditorIfMentionListboxOpened() {
        if (await this.isMentionListboxOpened()) {
            this.richTextEditorElement.tiptapEditor.commands.focus();
            await waitForUpdatesAsync();
        }
    }
}
//# sourceMappingURL=rich-text-editor.pageobject.js.map