import OrderedMap from 'orderedmap';
import { inputRules } from 'prosemirror-inputrules';
import { keymap } from 'prosemirror-keymap';
var Priority;
(function (Priority) {
    Priority[Priority["Highest"] = 1000000] = "Highest";
    Priority[Priority["VeryHigh"] = 100000] = "VeryHigh";
    Priority[Priority["High"] = 10000] = "High";
    Priority[Priority["Medium"] = 1000] = "Medium";
    Priority[Priority["Low"] = 100] = "Low";
    Priority[Priority["VeryLow"] = 10] = "VeryLow";
    Priority[Priority["Lowest"] = 0] = "Lowest";
})(Priority || (Priority = {}));
const DEFAULT_PRIORITY = Priority.Medium;
export class ExtensionBuilder {
    static createContext() {
        return new Map();
    }
    /* eslint-disable @typescript-eslint/member-ordering */
    static Priority = Priority;
    Priority = ExtensionBuilder.Priority;
    /* eslint-enable @typescript-eslint/member-ordering */
    #logger;
    #confMdCbs = [];
    #nodeSpecs = {};
    #markSpecs = {};
    #plugins = [];
    #actions = [];
    context;
    constructor(logger, context) {
        this.#logger = logger;
        this.context = context ?? ExtensionBuilder.createContext();
    }
    get logger() {
        return this.#logger;
    }
    use(extension, ...params) {
        extension(this, ...params);
        return this;
    }
    configureMd(cb, params = {}) {
        this.#confMdCbs.push({
            cb,
            params: {
                text: params.text ?? true,
                markup: params.markup ?? true,
            },
        });
        return this;
    }
    addNode(name, cb) {
        if (this.#nodeSpecs[name]) {
            throw new Error(`ProseMirror node with this name "${name}" already exist`);
        }
        this.#nodeSpecs[name] = { name, cb };
        return this;
    }
    addMark(name, cb, priority = DEFAULT_PRIORITY) {
        if (this.#markSpecs[name]) {
            throw new Error(`ProseMirror mark with this name "${name}" already exist`);
        }
        this.#markSpecs[name] = { name, cb, priority };
        return this;
    }
    addPlugin(cb, priority = DEFAULT_PRIORITY) {
        this.#plugins.push({ cb, priority });
        return this;
    }
    addKeymap(cb, priority = DEFAULT_PRIORITY) {
        this.#plugins.push({ cb: (...args) => keymap(cb(...args)), priority });
        return this;
    }
    addInputRules(cb, priority = DEFAULT_PRIORITY) {
        this.#plugins.push({ cb: (...args) => inputRules(cb(...args)), priority });
        return this;
    }
    addAction(name, cb) {
        if (this.#actions.some(([actionName]) => actionName === name)) {
            throw new Error(`[Markdown Wysiwyg Editor] action with this name "${name}" already exist`);
        }
        this.#actions.push([name, cb]);
        return this;
    }
    build() {
        const confMd = this.#confMdCbs.slice();
        const nodes = { ...this.#nodeSpecs };
        const marks = { ...this.#markSpecs };
        const plugins = this.#plugins.slice();
        const actions = this.#actions.slice();
        return {
            configureMd: (md, parserType) => confMd.reduce((pMd, { cb, params }) => {
                if (parserType === 'text' && params.text) {
                    return cb(pMd);
                }
                if (parserType === 'markup' && params.markup) {
                    return cb(pMd);
                }
                return pMd;
            }, md),
            nodes: () => {
                let map = OrderedMap.from({});
                for (const { name, cb } of Object.values(nodes)) {
                    map = map.addToEnd(name, cb());
                }
                return map;
            },
            marks: () => {
                // The order of marks in schema is important when serializing pm-document to DOM or markup
                // https://discuss.prosemirror.net/t/marks-priority/4463
                const sortedMarks = Object.values(marks).sort((a, b) => b.priority - a.priority);
                let map = OrderedMap.from({});
                for (const { name, cb } of sortedMarks) {
                    map = map.addToEnd(name, cb());
                }
                return map;
            },
            plugins: (deps) => {
                return plugins
                    .sort((a, b) => b.priority - a.priority)
                    .reduce((acc, { cb }) => {
                    const res = cb(deps);
                    if (Array.isArray(res))
                        acc.push(...res);
                    else
                        acc.push(res);
                    return acc;
                }, []);
            },
            actions: (deps) => actions.reduce((obj, [name, cb]) => {
                obj[name] = cb(deps);
                return obj;
            }, {}),
        };
    }
}
//# sourceMappingURL=ExtensionBuilder.js.map
