import { UmbBlockElementPropertyDatasetContext } from './block-element-property-dataset.context.js';
import { UmbContentTypeStructureManager } from '../../../content/content-type/index.js';
import { UmbClassState, appendToFrozenArray, mergeObservables, } from '../../../../libs/observable-api/index.js';
import { UmbControllerBase } from '../../../../libs/class-api/index.js';
import { UmbDocumentTypeDetailRepository } from '../../../documents/document-types/index.js';
import { UmbVariantId } from '../../../core/variant/index.js';
import { UmbValidationController } from '../../../core/validation/index.js';
import { UmbContentValidationToHintsManager, UmbElementWorkspaceDataManager, } from '../../../content/content/index.js';
import { UmbReadOnlyVariantGuardManager } from '../../../core/utils/index.js';
import { UmbDataTypeItemRepositoryManager } from '../../../data-type/index.js';
import { UmbVariantPropertyGuardManager } from '../../../core/property/index.js';
import { UmbViewContext } from '../../../core/view/index.js';
export class UmbBlockElementManager extends UmbControllerBase {
    //
    #data;
    #getDataPromise;
    #getDataResolver;
    #variantId;
    getValues() {
        return this.#data.getCurrent()?.values;
    }
    #dataTypeItemManager;
    constructor(host, dataPathPropertyName, workspaceViewAlias) {
        super(host);
        //
        this.#data = new UmbElementWorkspaceDataManager(this);
        //#data = new UmbObjectState<UmbBlockDataModel | undefined>(undefined);
        this.data = this.#data.current;
        this.#getDataPromise = new Promise((resolve) => {
            this.#getDataResolver = resolve;
        });
        // TODO: who is controlling this? We need to be aware about seperation of concerns. [NL]
        this.readOnlyGuard = new UmbReadOnlyVariantGuardManager(this);
        this.#variantId = new UmbClassState(undefined);
        this.variantId = this.#variantId.asObservable();
        this.unique = this.#data.createObservablePartOfCurrent((data) => data?.key);
        this.contentTypeId = this.#data.createObservablePartOfCurrent((data) => data?.contentTypeKey);
        this.values = this.#data.createObservablePartOfCurrent((data) => data?.values);
        this.#dataTypeItemManager = new UmbDataTypeItemRepositoryManager(this);
        this.structure = new UmbContentTypeStructureManager(this, new UmbDocumentTypeDetailRepository(this));
        this.propertyViewGuard = new UmbVariantPropertyGuardManager(this);
        this.propertyWriteGuard = new UmbVariantPropertyGuardManager(this);
        this.validation = new UmbValidationController(this);
        this.finishPropertyValueChange = () => {
            this.#data.finishPropertyValueChange();
        };
        this.view = new UmbViewContext(this, workspaceViewAlias);
        new UmbContentValidationToHintsManager(this, this.structure, this.validation, this.view.hints, []);
        // Ugly, but we just inherit these from the workspace context: [NL]
        this.name = host.name;
        this.getName = () => {
            const contentTypeLabel = this.structure.getOwnerContentType()?.name;
            const blockLabel = host.getName();
            return contentTypeLabel ? `${contentTypeLabel} ${blockLabel}` : blockLabel;
        };
        this.propertyViewGuard.fallbackToPermitted();
        this.propertyWriteGuard.fallbackToPermitted();
        this.observe(this.contentTypeId, (id) => {
            if (id) {
                this.structure.loadType(id);
            }
        });
        this.observe(this.unique, (key) => {
            if (key) {
                this.validation.setDataPath('$.' + dataPathPropertyName + `[?(@.key == '${key}')]`);
            }
        });
        this.observe(this.structure.contentTypeDataTypeUniques, (dataTypeUniques) => {
            this.#dataTypeItemManager.setUniques(dataTypeUniques);
        }, null);
    }
    isLoaded() {
        return this.#getDataPromise;
    }
    resetState() {
        this.#data.clear();
        this.propertyViewGuard.clearRules();
        this.propertyWriteGuard.clearRules();
        // default:
        this.propertyViewGuard.fallbackToPermitted();
        this.propertyWriteGuard.fallbackToPermitted();
    }
    setVariantId(variantId) {
        this.#variantId.setValue(variantId);
    }
    getVariantId() {
        return this.#variantId.getValue() ?? UmbVariantId.CreateInvariant();
    }
    // TODO: rename to currentData:
    setData(data) {
        this.#data.setPersisted(data);
        this.#data.setCurrent(data);
        this.#getDataResolver();
    }
    getData() {
        return this.#data.getCurrent();
    }
    setPersistedData(data) {
        this.#data.setPersisted(data);
    }
    /**
     * Check if there are unpersisted changes.
     * @returns { boolean } true if there are unpersisted changes.
     */
    getHasUnpersistedChanges() {
        return this.#data.getHasUnpersistedChanges();
    }
    getUnique() {
        return this.getData()?.key;
    }
    getEntityType() {
        return 'element';
    }
    getContentTypeId() {
        return this.getData()?.contentTypeKey;
    }
    #createPropertyVariantId(property, variantId) {
        return variantId.toVariant(property.variesByCulture, property.variesBySegment);
    }
    // We will implement propertyAlias in the future, when implementing Varying Blocks. [NL]
    async propertyVariantId(propertyAlias) {
        return mergeObservables([await this.structure.propertyStructureByAlias(propertyAlias), this.variantId], ([property, variantId]) => property && variantId ? this.#createPropertyVariantId(property, variantId) : undefined);
    }
    /**
     * @function propertyValueByAlias
     * @param {string} propertyAlias - The alias of the property
     * @param {UmbVariantId} variantId - The variant
     * @returns {Promise<Observable<ReturnType | undefined> | undefined>} - An observable for the value of the property
     * @description Get an Observable for the value of this property.
     */
    async propertyValueByAlias(propertyAlias, variantId) {
        return this.#data.createObservablePartOfCurrent((data) => data?.values?.find((x) => x?.alias === propertyAlias && (variantId ? variantId.compare(x) : true))
            ?.value);
    }
    /**
     * Get the current value of the property with the given alias and variantId.
     * @param {string} alias - The alias of the property
     * @param {UmbVariantId | undefined} variantId - The variant id of the property
     * @returns {ReturnType | undefined} The value or undefined if not set or found.
     */
    getPropertyValue(alias, variantId) {
        const currentData = this.#data.getCurrent();
        if (currentData) {
            const newDataSet = currentData.values?.find((x) => x.alias === alias && (variantId ? variantId.compare(x) : true));
            return newDataSet?.value;
        }
        return undefined;
    }
    async setPropertyValue(alias, value, variantId) {
        this.initiatePropertyValueChange();
        variantId ??= UmbVariantId.CreateInvariant();
        const property = await this.structure.getPropertyStructureByAlias(alias);
        if (!property) {
            throw new Error(`Property alias "${alias}" not found.`);
        }
        const dataTypeItem = await this.#dataTypeItemManager.getItemByUnique(property.dataType.unique);
        const editorAlias = dataTypeItem?.propertyEditorSchemaAlias;
        if (!editorAlias) {
            throw new Error(`Editor Alias of "${property.dataType.unique}" not found.`);
        }
        const entry = { editorAlias, ...variantId.toObject(), alias, value };
        const currentData = this.getData();
        if (currentData) {
            const values = appendToFrozenArray(currentData.values ?? [], entry, (x) => x.alias === alias && variantId.compare(x));
            this.#data.updateCurrent({ values });
        }
        this.finishPropertyValueChange();
    }
    initiatePropertyValueChange() {
        this.#data.initiatePropertyValueChange();
    }
    createPropertyDatasetContext(host, variantId) {
        return new UmbBlockElementPropertyDatasetContext(host, this, variantId);
    }
    setup(host, variantId) {
        this.createPropertyDatasetContext(host, variantId);
        // Provide Validation Context for this view:
        this.validation.provideAt(host);
        this.view.provideAt(host);
    }
    destroy() {
        this.structure.destroy();
        super.destroy();
    }
}
export default UmbBlockElementManager;
