var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { UmbBlockListManagerContext } from '../../context/block-list-manager.context.js';
import { UmbBlockListEntriesContext } from '../../context/block-list-entries.context.js';
import { UMB_BLOCK_LIST_PROPERTY_EDITOR_SCHEMA_ALIAS } from './constants.js';
import { UmbLitElement, umbDestroyOnDisconnect } from '../../../../core/lit-element/index.js';
import { html, customElement, property, state, repeat, css, nothing, ifDefined, } from '../../../../../external/lit/index.js';
import { debounceTime } from '../../../../../external/rxjs/index.js';
import { extractJsonQueryProps, UmbFormControlMixin, UmbValidationContext, UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, } from '../../../../core/validation/index.js';
import { jsonStringComparison, observeMultiple } from '../../../../../libs/observable-api/index.js';
import { UmbSorterController } from '../../../../core/sorter/index.js';
import { UMB_PROPERTY_CONTEXT } from '../../../../core/property/index.js';
import { UMB_CONTENT_WORKSPACE_CONTEXT } from '../../../../content/content/index.js';
import { UMB_VARIANT_CONTEXT } from '../../../../core/variant/index.js';
import '../../components/block-list-entry/index.js';
const SORTER_CONFIG = {
    getUniqueOfElement: (element) => {
        return element.contentKey;
    },
    getUniqueOfModel: (modelEntry) => {
        return modelEntry.contentKey;
    },
    //identifier: 'block-list-editor',
    itemSelector: 'umb-block-list-entry',
    //containerSelector: 'EMPTY ON PURPOSE, SO IT BECOMES THE HOST ELEMENT',
};
let UmbPropertyEditorUIBlockListElement = class UmbPropertyEditorUIBlockListElement extends UmbFormControlMixin(UmbLitElement) {
    #sorter;
    #validationContext;
    #lastValue;
    set value(value) {
        this.#lastValue = value;
        if (!value) {
            super.value = undefined;
            // Clear manager state so blocks are actually removed
            this.#managerContext.setLayouts([]);
            this.#managerContext.setContents([]);
            this.#managerContext.setSettings([]);
            this.#managerContext.setExposes([]);
            return;
        }
        const buildUpValue = value ? { ...value } : {};
        buildUpValue.layout ??= {};
        buildUpValue.contentData ??= [];
        buildUpValue.settingsData ??= [];
        buildUpValue.expose ??= [];
        super.value = buildUpValue;
        this.#managerContext.setLayouts(super.value.layout[UMB_BLOCK_LIST_PROPERTY_EDITOR_SCHEMA_ALIAS] ?? []);
        this.#managerContext.setContents(super.value.contentData);
        this.#managerContext.setSettings(super.value.settingsData);
        this.#managerContext.setExposes(super.value.expose);
    }
    get value() {
        return super.value;
    }
    set config(config) {
        if (!config)
            return;
        const validationLimit = config.getValueByAlias('validationLimit');
        this._limitMin = validationLimit?.min;
        this._limitMax = validationLimit?.max;
        const blocks = config.getValueByAlias('blocks') ?? [];
        this.#managerContext.setBlockTypes(blocks);
        const useInlineEditingAsDefault = config.getValueByAlias('useInlineEditingAsDefault');
        this.#managerContext.setInlineEditingMode(useInlineEditingAsDefault);
        this.style.maxWidth = config.getValueByAlias('maxPropertyWidth') ?? '';
        this.#managerContext.setEditorConfiguration(config);
        const customCreateButtonLabel = config.getValueByAlias('createLabel');
        if (customCreateButtonLabel) {
            this._createButtonLabel = this.localize.string(customCreateButtonLabel);
        }
        else if (blocks.length === 1) {
            this.#managerContext.contentTypesLoaded.then(() => {
                const firstContentTypeName = this.#managerContext.getContentTypeNameOf(blocks[0].contentElementTypeKey);
                this._createButtonLabel = this.localize.term('blockEditor_addThis', this.localize.string(firstContentTypeName));
                // If we are in a invariant context:
            });
        }
    }
    /**
     * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
     * @type {boolean}
     * @default
     */
    set readonly(value) {
        this.#readonly = value;
        if (this.#readonly) {
            this.#sorter.disable();
            this.#managerContext.readOnlyState.fallbackToPermitted();
        }
        else {
            this.#sorter.enable();
            this.#managerContext.readOnlyState.fallbackToNotPermitted();
        }
    }
    get readonly() {
        return this.#readonly;
    }
    #readonly;
    #managerContext;
    #entriesContext;
    constructor() {
        super();
        this.#sorter = new UmbSorterController(this, {
            ...SORTER_CONFIG,
            onChange: ({ model }) => {
                this.#entriesContext.setLayouts(model);
            },
        });
        this.#validationContext = new UmbValidationContext(this);
        this.#lastValue = undefined;
        this._createButtonLabel = this.localize.term('blockEditor_addBlock');
        this.#readonly = false;
        this._layouts = [];
        this.#managerContext = new UmbBlockListManagerContext(this);
        this.#entriesContext = new UmbBlockListEntriesContext(this);
        this._isSortMode = false;
        this.consumeContext(UMB_CONTENT_WORKSPACE_CONTEXT, (context) => {
            if (context) {
                this.observe(observeMultiple([
                    this.#managerContext.blockTypes,
                    context.structure.variesByCulture,
                    context.structure.variesBySegment,
                ]), async ([blockTypes, variesByCulture, variesBySegment]) => {
                    if (blockTypes.length > 0 && (variesByCulture === false || variesBySegment === false)) {
                        // check if any of the Blocks varyByCulture or Segment and then display a warning.
                        const promises = await Promise.all(blockTypes.map(async (blockType) => {
                            const elementType = blockType.contentElementTypeKey;
                            await this.#managerContext.contentTypesLoaded;
                            const structure = await this.#managerContext.getStructure(elementType);
                            if (variesByCulture === false && structure?.getVariesByCulture() === true) {
                                // If block varies by culture but document does not.
                                return true;
                            }
                            else if (variesBySegment === false && structure?.getVariesBySegment() === true) {
                                // If block varies by segment but document does not.
                                return true;
                            }
                            return false;
                        }));
                        this._notSupportedVariantSetting = promises.filter((x) => x === true).length > 0;
                        if (this._notSupportedVariantSetting) {
                            this.#validationContext.messages.addMessage('config', '$', '#blockEditor_blockVariantConfigurationNotSupported', 'blockConfigurationNotSupported');
                        }
                    }
                }, 'blockTypeConfigurationCheck');
            }
            else {
                this.removeUmbControllerByAlias('blockTypeConfigurationCheck');
            }
        }).passContextAliasMatches();
        this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => {
            this.#gotPropertyContext(context);
        });
        // TODO: Why is this logic not part of the Block Grid and RTE Editors? [NL]
        // Observe Blocks and clean up validation messages for content/settings that are not in the block list anymore:
        this.observe(this.#managerContext.layouts, (layouts) => {
            const validationMessagesToRemove = [];
            const contentKeys = layouts.map((x) => x.contentKey);
            this.#validationContext.messages.getMessagesOfPathAndDescendant('$.contentData').forEach((message) => {
                // get the KEY from this string: $.contentData[?(@.key == 'KEY')]
                // TODO: Investigate if this is missing a part to just get the [] part of the path. Cause couldn't there be a sub path inside of this. [NL]
                const key = extractJsonQueryProps(message.path).key;
                if (key && contentKeys.indexOf(key) === -1) {
                    validationMessagesToRemove.push(message.key);
                }
            });
            const settingsKeys = layouts.map((x) => x.settingsKey).filter((x) => x !== undefined);
            this.#validationContext.messages.getMessagesOfPathAndDescendant('$.settingsData').forEach((message) => {
                // TODO: Investigate if this is missing a part to just get the [] part of the path. Cause couldn't there be a sub path inside of this. [NL]
                // get the key from this string: $.settingsData[?(@.key == 'KEY')]
                const key = extractJsonQueryProps(message.path).key;
                if (key && settingsKeys.indexOf(key) === -1) {
                    validationMessagesToRemove.push(message.key);
                }
            });
            // Remove the messages after the loop to prevent changing the array while iterating over it.
            this.#validationContext.messages.removeMessageByKeys(validationMessagesToRemove);
        }, null);
        this.consumeContext(UMB_VARIANT_CONTEXT, async (context) => {
            this.observe(context?.displayVariantId, (variantId) => {
                this.#managerContext.setVariantId(variantId);
            }, 'observeContextualVariantId');
        });
        this.observe(this.#managerContext.isSortMode, (isSortMode) => (this._isSortMode = isSortMode ?? false));
        this.addValidator('rangeUnderflow', () => this.localize.term('validation_entriesShort', this._limitMin, (this._limitMin ?? 0) - this.#entriesContext.getLength()), () => !!this._limitMin && this.#entriesContext.getLength() < this._limitMin);
        this.addValidator('rangeOverflow', () => this.localize.term('validation_entriesExceed', this._limitMax, this.#entriesContext.getLength() - (this._limitMax || 0)), () => !!this._limitMax && this.#entriesContext.getLength() > this._limitMax);
        this.addValidator('valueMissing', () => this.mandatoryMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, () => {
            if (!this.mandatory || this.readonly)
                return false;
            const count = this.value?.layout?.[UMB_BLOCK_LIST_PROPERTY_EDITOR_SCHEMA_ALIAS]?.length ?? 0;
            return count === 0;
        });
        this.observe(this.#entriesContext.layoutEntries, (layouts) => {
            this._layouts = layouts;
            // Update sorter.
            this.#sorter.setModel(layouts);
            // Update manager:
            this.#managerContext.setLayouts(layouts);
        }, null);
        this.observe(this.#managerContext.blockTypes, (blockTypes) => {
            this._blocks = blockTypes;
        }, null);
        this.observe(this.#entriesContext.catalogueRouteBuilder, (routeBuilder) => {
            this._catalogueRouteBuilder = routeBuilder;
        }, null);
    }
    #gotPropertyContext(context) {
        this.observe(context?.dataPath, (dataPath) => {
            if (dataPath) {
                // Set the data path for the local validation context:
                this.#validationContext.setDataPath(dataPath);
                this.#validationContext.autoReport();
            }
        }, 'observeDataPath');
        this.observe(context?.alias, (alias) => {
            this.#managerContext.setPropertyAlias(alias);
        }, 'observePropertyAlias');
        this.observe(observeMultiple([
            this.#managerContext.layouts,
            this.#managerContext.contents,
            this.#managerContext.settings,
            this.#managerContext.exposes,
        ]).pipe(debounceTime(20)), ([layouts, contents, settings, exposes]) => {
            if (layouts.length === 0) {
                if (this.value === undefined) {
                    return;
                }
                super.value = undefined;
            }
            else {
                const newValue = {
                    ...super.value,
                    layout: { [UMB_BLOCK_LIST_PROPERTY_EDITOR_SCHEMA_ALIAS]: layouts },
                    contentData: contents,
                    settingsData: settings,
                    expose: exposes,
                };
                if (jsonStringComparison(this.value, newValue)) {
                    return;
                }
                super.value = newValue;
            }
            // If we don't have a value set from the outside or an internal value, we don't want to set the value.
            // This is added to prevent the block list from setting an empty value on startup.
            if (this.#lastValue === undefined && super.value === undefined) {
                return;
            }
            context?.setValue(super.value);
        }, 'motherObserver');
    }
    getFormElement() {
        return undefined;
    }
    render() {
        if (this._notSupportedVariantSetting)
            return nothing;
        return html `
			${this.#renderSortModeToolbar()}
			${repeat(this._layouts, (layout) => layout.contentKey, (layout, index) => html `
					${this.#renderInlineCreateButton(index)}
					<umb-block-list-entry
						index=${index}
						.contentKey=${layout.contentKey}
						.layout=${layout}
						${umbDestroyOnDisconnect()}>
					</umb-block-list-entry>
				`)}
			${this.#renderCreateButtonGroup()}
		`;
    }
    #renderCreateButtonGroup() {
        if (this._layouts.length > 0 && (this._limitMax === 1 || this.readonly))
            return nothing;
        return html `<uui-button-group>${this.#renderCreateButton()}${this.#renderPasteButton()}</uui-button-group>`;
    }
    #renderInlineCreateButton(index) {
        if (this.readonly)
            return nothing;
        const createPath = this.#entriesContext.getPathForCreateBlock(index);
        return html `
			<uui-button-inline-create
				label=${this._createButtonLabel}
				href=${ifDefined(createPath)}
				@click=${() => {
            // If no path, then we can conclude there is not modal flow for the user to follow, instead we will just insert the Block: [NL]
            if (createPath === undefined) {
                this.#handleCreateWithNoCreatePath(index);
            }
        }}>
			</uui-button-inline-create>
		`;
    }
    #renderCreateButton() {
        if (!this._catalogueRouteBuilder)
            return nothing;
        const createPath = this.#entriesContext.getPathForCreateBlock(-1);
        return html `
			<uui-button
				look="placeholder"
				.label=${this._createButtonLabel}
				href=${ifDefined(createPath)}
				?disabled=${this.readonly}
				@click=${() => {
            // If no path, then we can conclude there is not modal flow for the user to follow, instead we will just insert the Block: [NL]
            if (createPath === undefined) {
                this.#handleCreateWithNoCreatePath();
            }
        }}></uui-button>
		`;
    }
    async #handleCreateWithNoCreatePath(index) {
        if (!this._blocks || this._blocks.length === 0) {
            throw new Error('No block types are configured for this Block List property editor');
        }
        if (index === undefined) {
            index = -1;
        }
        const originData = { index };
        const created = await this.#entriesContext.create(this._blocks[0].contentElementTypeKey, {}, originData);
        if (created) {
            this.#entriesContext.insert(created.layout, created.content, created.settings, originData);
        }
        else {
            throw new Error('Failed to create block');
        }
    }
    #renderPasteButton() {
        return html `
			<uui-button
				label=${this.localize.term('content_createFromClipboard')}
				look="placeholder"
				href=${this._catalogueRouteBuilder?.({ view: 'clipboard', index: -1 }) ?? ''}
				?disabled=${this.readonly}
				title=${this.localize.term('general_clipboard')}>
				<uui-icon name="icon-clipboard-paste"></uui-icon>
			</uui-button>
		`;
    }
    #renderSortModeToolbar() {
        if (!this._isSortMode)
            return nothing;
        return html `<umb-property-sort-mode-toolbar></umb-property-sort-mode-toolbar>`;
    }
    static { this.styles = [
        css `
			uui-button-group {
				margin-top: 1px;
				display: grid;
				grid-template-columns: 1fr auto;
			}
		`,
    ]; }
};
__decorate([
    property({ attribute: false })
], UmbPropertyEditorUIBlockListElement.prototype, "value", null);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_createButtonLabel", void 0);
__decorate([
    property({ type: Boolean })
], UmbPropertyEditorUIBlockListElement.prototype, "mandatory", void 0);
__decorate([
    property({ type: String })
], UmbPropertyEditorUIBlockListElement.prototype, "mandatoryMessage", void 0);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_limitMin", void 0);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_limitMax", void 0);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_blocks", void 0);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_layouts", void 0);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_catalogueRouteBuilder", void 0);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_notSupportedVariantSetting", void 0);
__decorate([
    state()
], UmbPropertyEditorUIBlockListElement.prototype, "_isSortMode", void 0);
UmbPropertyEditorUIBlockListElement = __decorate([
    customElement('umb-property-editor-ui-block-list')
], UmbPropertyEditorUIBlockListElement);
export { UmbPropertyEditorUIBlockListElement };
export default UmbPropertyEditorUIBlockListElement;
