// (C) 2019-2026 GoodData Corporation
import { difference } from "lodash-es";
import { invariant } from "ts-invariant";
import { resolveValueOrUpdateCallback } from "@gooddata/sdk-backend-base";
import { isDashboardLayout, } from "@gooddata/sdk-model";
import { getSectionIndex } from "../../../layout/coordinates.js";
import { DashboardLayoutFacade } from "../facade/layout.js";
import { DashboardLayoutItemBuilder } from "./item.js";
import { DashboardLayoutSectionBuilder } from "./section.js";
/**
 * @alpha
 */
export class DashboardLayoutBuilder {
    layoutFacade;
    layoutFacadeConstructor;
    constructor(layoutFacade, layoutFacadeConstructor) {
        this.layoutFacade = layoutFacade;
        this.layoutFacadeConstructor = layoutFacadeConstructor;
    }
    /**
     * Creates an instance of DashboardLayoutBuilder for particular layout.
     *
     * @param layout - layout to modify
     */
    static for(layout, layoutPath) {
        invariant(isDashboardLayout(layout), "Provided data must be IDashboardLayout.");
        return new DashboardLayoutBuilder(DashboardLayoutFacade.for(layout, layoutPath), DashboardLayoutFacade.for);
    }
    /**
     * Creates an instance of DashboardLayoutBuilder with empty layout.
     *
     * @param layoutPath - layout path in case of nested layout
     */
    static forNewLayout(layoutPath) {
        const emptyLayout = {
            type: "IDashboardLayout",
            sections: [],
        };
        return DashboardLayoutBuilder.for(emptyLayout, layoutPath);
    }
    size(valueOrUpdateCallback) {
        return this.setLayout((layout) => ({
            ...layout,
            size: resolveValueOrUpdateCallback(valueOrUpdateCallback, this.facade().size()),
        }));
    }
    createSection(create = (v) => v, indexOrPath) {
        if (typeof indexOrPath === "number" || typeof indexOrPath === "undefined") {
            const index = indexOrPath ?? this.facade().sections().count();
            const emptySection = {
                type: "IDashboardLayoutSection",
                items: [],
            };
            this.setLayout((layout) => {
                const updatedRows = [...layout.sections];
                updatedRows.splice(index, 0, emptySection);
                return {
                    ...layout,
                    sections: updatedRows,
                };
            });
            DashboardLayoutSectionBuilder.for(this, index).modify(create);
        }
        else {
            this.createSectionByPath(create, indexOrPath);
        }
        return this;
    }
    createSectionByPath(create, sectionPath) {
        if (sectionPath.parent && sectionPath.parent.length > 0) {
            const [nextPathEntry, ...remainingPath] = sectionPath.parent;
            const nestedLayout = this.facade().nestedLayout([nextPathEntry])?.raw();
            invariant(nestedLayout, `Cannot create section - nested layout at path ${JSON.stringify(nextPathEntry)} does not exist.`);
            const sectionBuilder = DashboardLayoutSectionBuilder.for(this, nextPathEntry.sectionIndex);
            const modify = (nestedLayoutBuilder) => {
                return nestedLayoutBuilder.createSection(create, {
                    parent: remainingPath,
                    sectionIndex: sectionPath.sectionIndex,
                });
            };
            DashboardLayoutItemBuilder.for(sectionBuilder, nextPathEntry.itemIndex).modifyLayoutWidget(modify, DashboardLayoutBuilder.for);
        }
        else {
            this.createSection(create, sectionPath.sectionIndex);
        }
        return this;
    }
    addSection(section, indexOrPath) {
        if (typeof indexOrPath === "number" || typeof indexOrPath === "undefined") {
            const index = indexOrPath === undefined ? this.facade().sections().count() : indexOrPath;
            this.setLayout((layout) => {
                const updatedRows = [...layout.sections];
                updatedRows.splice(index, 0, section);
                return {
                    ...layout,
                    sections: updatedRows,
                };
            });
        }
        else {
            this.addSectionByPath(indexOrPath, section);
        }
        return this;
    }
    addSectionByPath(sectionPath, section) {
        if (sectionPath.parent && sectionPath.parent.length > 0) {
            const [nextPathEntry, ...remainingPath] = sectionPath.parent;
            const nestedLayout = this.facade().nestedLayout([nextPathEntry])?.raw();
            invariant(nestedLayout, `Cannot add section - nested layout at path ${JSON.stringify(nextPathEntry)} does not exist.`);
            const sectionBuilder = DashboardLayoutSectionBuilder.for(this, nextPathEntry.sectionIndex);
            const modify = (nestedLayoutBuilder) => {
                return nestedLayoutBuilder.addSection(section, {
                    parent: remainingPath,
                    sectionIndex: sectionPath.sectionIndex,
                });
            };
            DashboardLayoutItemBuilder.for(sectionBuilder, nextPathEntry.itemIndex).modifyLayoutWidget(modify, DashboardLayoutBuilder.for);
        }
        else {
            this.addSection(section, sectionPath.sectionIndex);
        }
        return this;
    }
    modifySection(indexOrPath, modify) {
        if (typeof indexOrPath === "number") {
            const sectionFacade = this.facade().sections().section(indexOrPath);
            invariant(sectionFacade, `Cannot modify the section - section at index ${indexOrPath} does not exist.`);
            DashboardLayoutSectionBuilder.for(this, indexOrPath).modify(modify);
        }
        else {
            this.modifySectionByPath(indexOrPath, modify);
        }
        return this;
    }
    modifySectionByPath(sectionPath, modify) {
        if (sectionPath.parent && sectionPath.parent.length > 0) {
            const [nextPathEntry, ...remainingPath] = sectionPath.parent;
            const nestedLayout = this.facade().nestedLayout([nextPathEntry])?.raw();
            invariant(nestedLayout, `Cannot modify section - nested layout at path ${JSON.stringify(nextPathEntry)} does not exist.`);
            const sectionBuilder = DashboardLayoutSectionBuilder.for(this, nextPathEntry.sectionIndex);
            const widgetModify = (nestedLayoutBuilder) => {
                return nestedLayoutBuilder.modifySection({
                    parent: remainingPath,
                    sectionIndex: sectionPath.sectionIndex,
                }, modify);
            };
            DashboardLayoutItemBuilder.for(sectionBuilder, nextPathEntry.itemIndex).modifyLayoutWidget(widgetModify, DashboardLayoutBuilder.for);
        }
        else {
            this.modifySection(sectionPath.sectionIndex, modify);
        }
        return this;
    }
    removeSection(indexOrPath) {
        if (typeof indexOrPath === "number") {
            const sectionFacade = this.facade().sections().section(indexOrPath);
            invariant(sectionFacade, `Cannot remove the section - section at index ${indexOrPath} does not exist.`);
            return this.setLayout((layout) => {
                const updatedRows = [...layout.sections];
                updatedRows.splice(indexOrPath, 1);
                return {
                    ...layout,
                    sections: updatedRows,
                };
            });
        }
        return this.removeSectionByPath(indexOrPath);
    }
    removeSectionByPath(sectionPath) {
        if (sectionPath.parent && sectionPath.parent.length > 0) {
            const [nextPathEntry, ...remainingPath] = sectionPath.parent;
            const nestedLayout = this.facade().nestedLayout([nextPathEntry])?.raw();
            invariant(nestedLayout, `Cannot modify section - nested layout at path ${JSON.stringify(nextPathEntry)} does not exist.`);
            const sectionBuilder = DashboardLayoutSectionBuilder.for(this, nextPathEntry.sectionIndex);
            const widgetModify = (nestedLayoutBuilder) => {
                return nestedLayoutBuilder.removeSection({
                    parent: remainingPath,
                    sectionIndex: sectionPath.sectionIndex,
                });
            };
            DashboardLayoutItemBuilder.for(sectionBuilder, nextPathEntry.itemIndex).modifyLayoutWidget(widgetModify, DashboardLayoutBuilder.for);
            return this;
        }
        else {
            return this.removeSection(sectionPath.sectionIndex);
        }
    }
    moveSection(fromIndexOrPath, toIndexOrPath) {
        if (typeof fromIndexOrPath === "number" && typeof toIndexOrPath === "number") {
            const section = this.facade().section(fromIndexOrPath)?.raw();
            invariant(section, `Cannot move the section - section at index ${fromIndexOrPath} does not exist.`);
            const maxToIndex = Math.min(toIndexOrPath, this.facade().sections().count() - 1);
            this.removeSection(fromIndexOrPath);
            this.createSection((r) => {
                return r.setSection(section);
            }, maxToIndex);
        }
        else {
            this.moveSectionByPath(fromIndexOrPath, toIndexOrPath);
        }
        return this;
    }
    moveSectionByPath(fromIndexOrPath, toIndexOrPath) {
        const section = typeof fromIndexOrPath === "number"
            ? this.facade().section(fromIndexOrPath)?.raw()
            : this.facade()
                .nestedLayout(fromIndexOrPath.parent ?? [])
                ?.section(fromIndexOrPath.sectionIndex)
                ?.raw();
        invariant(section, `Cannot move the section - section at index ${JSON.stringify(fromIndexOrPath)} does not exist.`);
        this.removeSection(fromIndexOrPath);
        this.createSection((r) => {
            return r.setSection(section);
        }, toIndexOrPath);
        return this;
    }
    removeSections(selector = (sections) => sections.all()) {
        const sectionsToRemove = selector(this.facade().sections());
        if (Array.isArray(sectionsToRemove)) {
            this.setLayout((layout) => {
                const updatedRows = difference(layout.sections, sectionsToRemove.map((r) => r.raw()));
                return {
                    ...layout,
                    sections: updatedRows,
                };
            });
        }
        else if (sectionsToRemove) {
            this.removeSection(getSectionIndex(sectionsToRemove.index()));
        }
        return this;
    }
    removeEmptySections() {
        return this.removeSections((sections) => sections.filter((section) => section.isEmpty()));
    }
    modifySections(modify, selector = (sections) => sections.all()) {
        const sectionsToModify = selector(this.facade().sections());
        if (Array.isArray(sectionsToModify)) {
            sectionsToModify.forEach((section) => {
                this.modifySection(getSectionIndex(section.index()), modify);
            });
        }
        else if (sectionsToModify) {
            this.modifySection(getSectionIndex(sectionsToModify.index()), modify);
        }
        return this;
    }
    setLayout(valueOrUpdateCallback) {
        const updatedLayout = resolveValueOrUpdateCallback(valueOrUpdateCallback, this.build());
        this.layoutFacade = this.layoutFacadeConstructor(updatedLayout, this.facade().path());
        return this;
    }
    facade() {
        return this.layoutFacade;
    }
    modify(modifications) {
        modifications(this, this.facade());
        return this;
    }
    build() {
        return this.layoutFacade.raw();
    }
}
//# sourceMappingURL=layout.js.map