/**
 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module paragraph/insertparagraphcommand
 */
import { Command } from '@ckeditor/ckeditor5-core';
/**
 * The insert paragraph command. It inserts a new paragraph at a specific
 * {@link module:engine/model/position~Position document position}.
 *
 * ```ts
 * // Insert a new paragraph before an element in the document.
 * editor.execute( 'insertParagraph', {
 *   position: editor.model.createPositionBefore( element )
 * } );
 * ```
 *
 * If a paragraph is disallowed in the context of the specific position, the command
 * will attempt to split position ancestors to find a place where it is possible
 * to insert a paragraph.
 *
 * **Note**: This command moves the selection to the inserted paragraph.
 */
export default class InsertParagraphCommand extends Command {
    constructor(editor) {
        super(editor);
        // Since this command passes position in execution block instead of selection, it should be checked directly.
        this._isEnabledBasedOnSelection = false;
    }
    /**
     * Executes the command.
     *
     * @param options Options for the executed command.
     * @param options.position The model position at which the new paragraph will be inserted.
     * @param options.attributes Attributes keys and values to set on a inserted paragraph.
     * @fires execute
     */
    execute(options) {
        const model = this.editor.model;
        const attributes = options.attributes;
        let position = options.position;
        // Don't execute command if position is in non-editable place.
        if (!model.canEditAt(position)) {
            return;
        }
        model.change(writer => {
            position = this._findPositionToInsertParagraph(position, writer);
            if (!position) {
                return;
            }
            const paragraph = writer.createElement('paragraph');
            if (attributes) {
                model.schema.setAllowedAttributes(paragraph, attributes, writer);
            }
            model.insertContent(paragraph, position);
            writer.setSelection(paragraph, 'in');
        });
    }
    /**
     * Returns the best position to insert a new paragraph.
     */
    _findPositionToInsertParagraph(position, writer) {
        const model = this.editor.model;
        if (model.schema.checkChild(position, 'paragraph')) {
            return position;
        }
        const allowedParent = model.schema.findAllowedParent(position, 'paragraph');
        // It could be there's no ancestor limit that would allow paragraph.
        // In theory, "paragraph" could be disallowed even in the "$root".
        if (!allowedParent) {
            return null;
        }
        const positionParent = position.parent;
        const isTextAllowed = model.schema.checkChild(positionParent, '$text');
        // At empty $block or at the end of $block.
        // <paragraph>[]</paragraph> ---> <paragraph></paragraph><paragraph>[]</paragraph>
        // <paragraph>foo[]</paragraph> ---> <paragraph>foo</paragraph><paragraph>[]</paragraph>
        if (positionParent.isEmpty || isTextAllowed && position.isAtEnd) {
            return model.createPositionAfter(positionParent);
        }
        // At the start of $block with text.
        // <paragraph>[]foo</paragraph> ---> <paragraph>[]</paragraph><paragraph>foo</paragraph>
        if (!positionParent.isEmpty && isTextAllowed && position.isAtStart) {
            return model.createPositionBefore(positionParent);
        }
        return writer.split(position, allowedParent).position;
    }
}
