import { UmbTreeItemActiveManager } from '../active-manager/tree-active-manager.js';
import { UmbTreeExpansionManager } from '../expansion-manager/index.js';
import { UmbTreeItemChildrenManager } from '../tree-item/tree-item-children.manager.js';
import { UmbTreeItemTargetExpansionManager } from '../tree-item/tree-item-expansion.manager.js';
import { UMB_TREE_CONTEXT } from '../tree.context.token.js';
import { UmbBooleanState, UmbObjectState } from '../../../../libs/observable-api/index.js';
import { UmbContextBase } from '../../../../libs/class-api/index.js';
import { UmbDeprecation, UmbSelectionManager, debounce } from '../../utils/index.js';
import { UmbExtensionApiInitializer } from '../../../../libs/extension-api/index.js';
import { umbExtensionsRegistry } from '../../extension-registry/index.js';
export class UmbDefaultTreeContext extends UmbContextBase {
    #treeRoot;
    #hideTreeRoot;
    #expandTreeRoot;
    #treeItemChildrenManager;
    #treeItemExpansionManager;
    #manifest;
    #repository;
    #initResolver;
    #initialized;
    #init;
    constructor(host) {
        super(host, UMB_TREE_CONTEXT);
        this.#treeRoot = new UmbObjectState(undefined);
        this.treeRoot = this.#treeRoot.asObservable();
        this.selectableFilter = () => true;
        this.filter = () => true;
        this.selection = new UmbSelectionManager(this);
        this.expansion = new UmbTreeExpansionManager(this);
        this.#hideTreeRoot = new UmbBooleanState(false);
        this.hideTreeRoot = this.#hideTreeRoot.asObservable();
        this.#expandTreeRoot = new UmbBooleanState(undefined);
        this.expandTreeRoot = this.#expandTreeRoot.asObservable();
        this.#treeItemChildrenManager = new UmbTreeItemChildrenManager(this);
        this.rootItems = this.#treeItemChildrenManager.children;
        this.hasChildren = this.#treeItemChildrenManager.hasChildren;
        this.pagination = this.#treeItemChildrenManager.offsetPagination;
        this.targetPagination = this.#treeItemChildrenManager.targetPagination;
        this.startNode = this.#treeItemChildrenManager.startNode;
        this.foldersOnly = this.#treeItemChildrenManager.foldersOnly;
        this.additionalRequestArgs = this.#treeItemChildrenManager.additionalRequestArgs;
        this.isLoadingPrevChildren = this.#treeItemChildrenManager.isLoadingPrevChildren;
        this.isLoadingNextChildren = this.#treeItemChildrenManager.isLoadingNextChildren;
        this.#treeItemExpansionManager = new UmbTreeItemTargetExpansionManager(this, {
            childrenManager: this.#treeItemChildrenManager,
            targetPaginationManager: this.targetPagination,
        });
        this.activeManager = new UmbTreeItemActiveManager(this);
        this.#initialized = false;
        this.#init = new Promise((resolve) => {
            if (this.#initialized) {
                resolve();
            }
            else {
                this.#initResolver = resolve;
            }
        });
        /**
         * Loads the tree
         * @memberof UmbDefaultTreeContext
         */
        // TODO: debouncing the load tree method because multiple props can be set at the same time
        // that would trigger multiple loadTree calls. This is a temporary solution to avoid that.
        this.loadTree = debounce(() => this.#debouncedLoadTree(), 100);
        /**
         * Reloads the tree in its current expansion state
         * @memberof UmbDefaultTreeContext
         */
        // TODO: debouncing the load tree method because multiple props can be set at the same time
        // that would trigger multiple loadTree calls. This is a temporary solution to avoid that.
        this.reloadTree = debounce(() => this.#debouncedLoadTree(true), 100);
        /**
         * Reloads the tree
         * @memberof UmbDefaultTreeContext
         * @returns {Promise<void>}
         */
        this.loadMore = () => this.#treeItemChildrenManager.loadNextChildren();
        /**
         * Load previous items of the tree item
         * @memberof UmbTreeItemContextBase
         * @returns {Promise<void>}
         */
        this.loadPrevItems = () => this.#treeItemChildrenManager.loadPrevChildren();
        /**
         * Load next items of the tree item
         * @memberof UmbTreeItemContextBase
         * @returns {Promise<void>}
         */
        this.loadNextItems = () => this.#treeItemChildrenManager.loadNextChildren();
        this.#treeItemChildrenManager.setTakeSize(50);
        // always load the tree root because we need the root entity to reload the entire tree
        this.#loadTreeRoot();
    }
    // TODO: find a generic way to do this
    #checkIfInitialized() {
        if (this.#repository) {
            this.#initialized = true;
            this.#initResolver?.();
        }
    }
    /**
     * Sets the manifest
     * @param {ManifestTree} manifest
     * @memberof UmbDefaultTreeContext
     */
    set manifest(manifest) {
        if (this.#manifest === manifest)
            return;
        this.#manifest = manifest;
        this.#observeRepository(this.#manifest?.meta.repositoryAlias);
    }
    get manifest() {
        return this.#manifest;
    }
    /**
     * Returns the manifest.
     * @returns {ManifestTree}
     * @memberof UmbDefaultTreeContext
     * @deprecated Use the `.manifest` property instead.
     */
    getManifest() {
        new UmbDeprecation({
            removeInVersion: '18.0.0',
            deprecated: 'getManifest',
            solution: 'Use .manifest property instead',
        }).warn();
        return this.#manifest;
    }
    getRepository() {
        return this.#repository;
    }
    async #debouncedLoadTree(reload = false) {
        await this.#init;
        const hasStartNode = this.getStartNode();
        const hideTreeRoot = this.getHideTreeRoot();
        if (hasStartNode || hideTreeRoot) {
            if (reload) {
                this.#treeItemChildrenManager.reloadChildren();
            }
            else {
                this.#treeItemChildrenManager.loadChildren();
            }
        }
        else {
            this.#loadTreeRoot(reload);
        }
    }
    async #loadTreeRoot(reload = false) {
        await this.#init;
        const { data } = await this.#repository.requestTreeRoot();
        if (data) {
            this.#treeRoot.setValue(data);
            this.#treeItemChildrenManager.setTreeItem(data);
            this.#treeItemExpansionManager.setTreeItem(data);
            this.pagination.setTotalItems(1);
            if (!reload) {
                if (this.getExpandTreeRoot()) {
                    this.#toggleTreeRootExpansion(true);
                }
            }
        }
    }
    /**
     * Sets the hideTreeRoot config
     * @param {boolean} hideTreeRoot - Whether to hide the tree root
     * @memberof UmbDefaultTreeContext
     */
    setHideTreeRoot(hideTreeRoot) {
        this.#hideTreeRoot.setValue(hideTreeRoot);
        // we need to reset the tree if this config changes
        this.#clearTree();
        this.loadTree();
    }
    /**
     * Gets the hideTreeRoot config
     * @returns {boolean}
     * @memberof UmbDefaultTreeContext
     */
    getHideTreeRoot() {
        return this.#hideTreeRoot.getValue();
    }
    /**
     * Sets the startNode config
     * @param {UmbTreeStartNode} startNode
     * @memberof UmbDefaultTreeContext
     */
    setStartNode(startNode) {
        this.#treeItemChildrenManager.setStartNode(startNode);
        // we need to reset the tree if this config changes
        this.#clearTree();
        this.loadTree();
    }
    /**
     * Gets the startNode config
     * @returns {UmbTreeStartNode} - The start node
     * @memberof UmbDefaultTreeContext
     */
    getStartNode() {
        return this.#treeItemChildrenManager.getStartNode();
    }
    /**
     * Sets the foldersOnly config
     * @param {boolean} foldersOnly - Whether to show only folders
     * @memberof UmbDefaultTreeContext
     */
    setFoldersOnly(foldersOnly) {
        this.#treeItemChildrenManager.setFoldersOnly(foldersOnly);
        // we need to reset the tree if this config changes
        this.#clearTree();
        this.loadTree();
    }
    /**
     * Gets the foldersOnly config
     * @returns {boolean} - Whether to show only folders
     * @memberof UmbDefaultTreeContext
     */
    getFoldersOnly() {
        return this.#treeItemChildrenManager.getFoldersOnly();
    }
    /**
     * Updates the requestArgs config and reloads the tree.
     * @param args
     */
    updateAdditionalRequestArgs(args) {
        this.#treeItemChildrenManager.setAdditionalRequestArgs(args);
        this.#clearTree();
        this.loadTree();
    }
    getAdditionalRequestArgs() {
        return this.#treeItemChildrenManager.getAdditionalRequestArgs();
    }
    /**
     * Sets the expansion state
     * @param {UmbTreeExpansionModel} data - The expansion state
     * @returns {void}
     * @memberof UmbDefaultTreeContext
     */
    setExpansion(data) {
        this.expansion.setExpansion(data);
    }
    /**
     * Gets the expansion state
     * @returns {UmbTreeExpansionModel} - The expansion state
     * @memberof UmbDefaultTreeContext
     */
    getExpansion() {
        return this.expansion.getExpansion();
    }
    /**
     * Sets the expandTreeRoot config
     * @param {boolean} expandTreeRoot - Whether to expand the tree root
     * @memberof UmbDefaultTreeContext
     */
    setExpandTreeRoot(expandTreeRoot) {
        this.#expandTreeRoot.setValue(expandTreeRoot);
        this.#toggleTreeRootExpansion(expandTreeRoot);
    }
    /**
     * Gets the expandTreeRoot config
     * @returns {boolean | undefined} - Whether to expand the tree root
     * @memberof UmbDefaultTreeContext
     */
    getExpandTreeRoot() {
        return this.#expandTreeRoot.getValue();
    }
    #toggleTreeRootExpansion(expand) {
        const treeRoot = this.#treeRoot.getValue();
        if (!treeRoot)
            return;
        const treeRootEntity = { entityType: treeRoot.entityType, unique: treeRoot.unique };
        if (expand) {
            this.expansion.expandItem(treeRootEntity);
        }
        else {
            this.expansion.collapseItem(treeRootEntity);
        }
    }
    #clearTree() {
        this.#treeRoot.setValue(undefined);
        this.#treeItemChildrenManager.clear();
    }
    #observeRepository(repositoryAlias) {
        if (!repositoryAlias)
            throw new Error('Tree must have a repository alias.');
        new UmbExtensionApiInitializer(this, umbExtensionsRegistry, repositoryAlias, [this], (permitted, ctrl) => {
            this.#repository = permitted ? ctrl.api : undefined;
            this.#checkIfInitialized();
        });
    }
    destroy() {
        this.loadTree.cancel();
        this.reloadTree.cancel();
        super.destroy();
    }
}
export { UmbDefaultTreeContext as api };
