/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Package, Unit, ROOT_PACKAGE_NAME, buildFunctionAnalysisInfoFromConcreteFunctionDefinition, getOrCreateGraphPackage, } from '@finos/legend-graph';
import { ActionState, addUniqueEntry, guaranteeNonNullable, isNonNullable, noop, uuid, } from '@finos/legend-shared';
import { action, makeObservable, observable } from 'mobx';
import { compareLabelFn, } from '@finos/legend-art';
export const QUERY_BUILDER_FUNCTION_DND_TYPE = 'QUERY_BUILDER_FUNCTION';
export class QueryBuilderFunctionsExplorerTreeNodeData {
    id;
    label;
    childrenIds = [];
    isOpen;
    package;
    functionAnalysisInfo;
    constructor(id, label) {
        this.id = id;
        this.label = label;
    }
}
const getValidDisplayablePackageSet = (queryBuilderState, rootPackageName) => {
    switch (rootPackageName) {
        case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
            return queryBuilderState.functionsExplorerState
                .dependencyDisplayablePackagesSet;
        default:
            return queryBuilderState.functionsExplorerState.displayablePackagesSet;
    }
};
const generateFunctionsExplorerTreeNodeDataFromPackage = (queryBuilderState, element, rootPackageName) => ({
    id: element.path,
    label: element.name,
    childrenIds: element.children
        .filter((child) => !(child instanceof Unit))
        .filter((child) => child instanceof Package &&
        getValidDisplayablePackageSet(queryBuilderState, rootPackageName).has(child))
        .map((child) => child.path)
        .concat(queryBuilderState.functionsExplorerState.packagePathToFunctionInfoMap
        ?.get(element.path)
        ?.map((info) => info.functionPath) ?? []),
    package: element,
});
export const generateFunctionsExplorerTreeNodeDataFromFunctionAnalysisInfo = (functionAnalysisInfo) => ({
    id: functionAnalysisInfo.functionPath,
    label: functionAnalysisInfo.name,
    childrenIds: [],
    functionAnalysisInfo: functionAnalysisInfo,
});
const generateFunctionsExplorerTreeNodeChilrdren = (queryBuilderState, node, data, rootPackageName = ROOT_PACKAGE_NAME.MAIN) => {
    const functionInfoMap = rootPackageName === ROOT_PACKAGE_NAME.MAIN
        ? queryBuilderState.functionsExplorerState.functionInfoMap
        : queryBuilderState.functionsExplorerState.dependencyFunctionInfoMap;
    const validDisplayablePackageSet = getValidDisplayablePackageSet(queryBuilderState, rootPackageName);
    const qualifiedExtraFunctionPaths = queryBuilderState.functionsExplorerState.packagePathToFunctionInfoMap
        ?.get(node.id)
        ?.map((info) => info.functionPath);
    const childrenPackages = node.package.children
        .filter((child) => 
    // avoid displaying empty packages
    child instanceof Package && validDisplayablePackageSet.has(child))
        .map((child) => child);
    node.childrenIds = childrenPackages
        .map((child) => child.path)
        .concat(qualifiedExtraFunctionPaths ?? []);
    const childNodesFromPackage = childrenPackages.map((child) => generateFunctionsExplorerTreeNodeDataFromPackage(queryBuilderState, child, rootPackageName));
    const childNodesFromFunction = qualifiedExtraFunctionPaths
        ?.map((path) => functionInfoMap?.get(path))
        .filter(isNonNullable)
        .map((info) => generateFunctionsExplorerTreeNodeDataFromFunctionAnalysisInfo(info));
    childNodesFromPackage
        .concat(childNodesFromFunction ?? [])
        .forEach((childNode) => {
        const currentNode = data.nodes.get(childNode.id);
        if (currentNode) {
            currentNode.childrenIds = childNode.childrenIds;
            currentNode.label = childNode.label;
        }
        else {
            data.nodes.set(childNode.id, childNode);
        }
    });
};
export const getFunctionsExplorerTreeData = (roots, queryBuilderState, rootPackageName = ROOT_PACKAGE_NAME.MAIN) => {
    const rootIds = [];
    const nodes = new Map();
    const validDisplayablePackageSet = getValidDisplayablePackageSet(queryBuilderState, rootPackageName);
    switch (rootPackageName) {
        case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
            if (!queryBuilderState.functionsExplorerState.dependencyFunctionInfoMap ||
                Array.from(queryBuilderState.functionsExplorerState.dependencyFunctionInfoMap).length === 0) {
                return { rootIds, nodes };
            }
            break;
        default:
            if (!queryBuilderState.functionsExplorerState.functionInfoMap ||
                Array.from(queryBuilderState.functionsExplorerState.functionInfoMap)
                    .length === 0) {
                return { rootIds, nodes };
            }
    }
    roots.forEach((root) => {
        root.children
            .slice()
            .filter((child) => child instanceof Package && validDisplayablePackageSet.has(child))
            .map((child) => child)
            .sort((a, b) => a.name.localeCompare(b.name))
            .forEach((childPackage) => {
            const childTreeNodeData = generateFunctionsExplorerTreeNodeDataFromPackage(queryBuilderState, childPackage, rootPackageName);
            addUniqueEntry(rootIds, childTreeNodeData.id);
            nodes.set(childTreeNodeData.id, childTreeNodeData);
        });
    });
    return { rootIds, nodes };
};
export const getFunctionsExplorerTreeNodeChildren = (queryBuilderState, node, data, rootPackageName = ROOT_PACKAGE_NAME.MAIN) => {
    generateFunctionsExplorerTreeNodeChilrdren(queryBuilderState, node, data, rootPackageName);
    return node.childrenIds
        .map((id) => data.nodes.get(id))
        .filter(isNonNullable)
        .sort(compareLabelFn)
        .sort((a, b) => (b.package ? 1 : 0) - (a.package ? 1 : 0));
};
const getAllPackagesFromElement = (element) => {
    if (element.package) {
        return (element instanceof Package ? [element] : []).concat([element.package].concat(getAllPackagesFromElement(element.package)));
    }
    return [];
};
export class QueryFunctionExplorerState {
    uuid = uuid();
    queryFunctionsState;
    functionAnalysisInfo;
    constructor(queryFunctionsState, functionAnalysisInfo) {
        makeObservable(this, {
            functionAnalysisInfo: observable,
        });
        this.queryFunctionsState = queryFunctionsState;
        this.functionAnalysisInfo = functionAnalysisInfo;
    }
}
export class QueryFunctionsExplorerState {
    initState = ActionState.create();
    queryBuilderState;
    treeData;
    dependencyTreeData;
    _functionGraph;
    functionExplorerStates = [];
    dependencyFunctionExplorerStates = [];
    displayablePackagesSet = new Set();
    dependencyDisplayablePackagesSet = new Set();
    functionInfoMap;
    dependencyFunctionInfoMap;
    packagePathToFunctionInfoMap;
    constructor(queryBuilderState) {
        makeObservable(this, {
            functionExplorerStates: observable.ref,
            setFunctionExplorerStates: action,
            dependencyFunctionExplorerStates: observable.ref,
            setDependencyFunctionExplorerStates: action,
            treeData: observable.ref,
            dependencyTreeData: observable.ref,
            _functionGraph: observable,
            functionInfoMap: observable,
            dependencyFunctionInfoMap: observable,
            packagePathToFunctionInfoMap: observable,
            setFunctionInfoMap: action,
            setDependencyFunctionInfoMap: action,
            setTreeData: action,
            setPackagePathToFunctionInfoMap: action,
            setDependencyTreeData: action,
            refreshTree: action,
            onTreeNodeSelect: action,
            initializeTreeData: action,
        });
        this.queryBuilderState = queryBuilderState;
        this._functionGraph =
            this.queryBuilderState.graphManagerState.createNewGraph();
    }
    getTreeData(rootPackageName = ROOT_PACKAGE_NAME.MAIN) {
        switch (rootPackageName) {
            case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
                return this.dependencyTreeData;
            default:
                return this.treeData;
        }
    }
    setFunctionExplorerStates(val) {
        this.functionExplorerStates = val;
    }
    setDependencyFunctionExplorerStates(val) {
        this.dependencyFunctionExplorerStates = val;
    }
    setFunctionInfoMap(info) {
        this.functionInfoMap = info;
    }
    setDependencyFunctionInfoMap(info) {
        this.dependencyFunctionInfoMap = info;
    }
    setPackagePathToFunctionInfoMap(map) {
        this.packagePathToFunctionInfoMap = map;
    }
    async initializeDisplayablePackagesSet() {
        if (this.functionInfoMap) {
            Array.from(this.functionInfoMap.values())
                .map((info) => getOrCreateGraphPackage(this._functionGraph, info.packagePath, undefined))
                .map((f) => getAllPackagesFromElement(f))
                .flat()
                .forEach((pkg) => this.displayablePackagesSet.add(pkg));
        }
    }
    async initializeDependencyDisplayablePackagesSet() {
        if (this.dependencyFunctionInfoMap) {
            Array.from(this.dependencyFunctionInfoMap.values())
                .map((info) => getOrCreateGraphPackage(this._functionGraph, info.packagePath, undefined))
                .map((f) => getAllPackagesFromElement(f))
                .flat()
                .forEach((pkg) => this.dependencyDisplayablePackagesSet.add(pkg));
        }
    }
    setTreeData(val) {
        this.treeData = val;
    }
    setDependencyTreeData(val) {
        this.dependencyTreeData = val;
    }
    refreshTree() {
        if (this.treeData) {
            this.treeData = { ...this.treeData };
        }
        if (this.dependencyTreeData) {
            this.dependencyTreeData = { ...this.dependencyTreeData };
        }
    }
    get nonNullableTreeData() {
        return guaranteeNonNullable(this.treeData, 'Query builder functions explorer tree data has not been initialized');
    }
    onTreeNodeSelect = (queryBuilderState, node, data, rootPackageName = ROOT_PACKAGE_NAME.MAIN) => {
        if (node.package) {
            if (node.childrenIds.length) {
                node.isOpen = !node.isOpen;
                generateFunctionsExplorerTreeNodeChilrdren(queryBuilderState, node, data, rootPackageName);
            }
            switch (rootPackageName) {
                case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
                    this.setDependencyTreeData({ ...data });
                    break;
                default:
                    this.setTreeData({ ...data });
            }
        }
    };
    initializeFunctionInfoMap() {
        const functionInfoMap = new Map();
        const dependencyFunctionInfoMap = new Map();
        const functionInfos = buildFunctionAnalysisInfoFromConcreteFunctionDefinition(this.queryBuilderState.graphManagerState.graph.ownFunctions, this._functionGraph);
        functionInfos.forEach((info) => functionInfoMap.set(info.functionPath, info));
        if (this.queryBuilderState.graphManagerState.graph.dependencyManager
            .hasDependencies) {
            const dependencyFunctions = this.queryBuilderState.graphManagerState.graph.dependencyManager
                .functions;
            const dependencyFunctionInfos = buildFunctionAnalysisInfoFromConcreteFunctionDefinition(dependencyFunctions, this._functionGraph);
            dependencyFunctionInfos.forEach((info) => dependencyFunctionInfoMap.set(info.functionPath, info));
        }
        const queryBuilderFunctionAnalysisInfo = this.queryBuilderState.buildFunctionAnalysisInfo();
        if (queryBuilderFunctionAnalysisInfo) {
            Array.from(queryBuilderFunctionAnalysisInfo.functionInfoMap.entries()).forEach(([path, info]) => functionInfoMap.set(path, info));
            Array.from(queryBuilderFunctionAnalysisInfo.dependencyFunctionInfoMap.entries()).forEach(([path, info]) => {
                dependencyFunctionInfoMap.set(path, info);
            });
        }
        const packagePathToFunctionInfoMap = new Map();
        Array.from(functionInfoMap.values())
            .concat(Array.from(dependencyFunctionInfoMap.values()))
            .forEach((info) => {
            const curr = packagePathToFunctionInfoMap.get(info.packagePath);
            if (curr) {
                packagePathToFunctionInfoMap.set(info.packagePath, [...curr, info]);
            }
            else {
                packagePathToFunctionInfoMap.set(info.packagePath, [info]);
            }
        });
        this.setPackagePathToFunctionInfoMap(packagePathToFunctionInfoMap);
        this.setFunctionInfoMap(functionInfoMap);
        this.setDependencyFunctionInfoMap(dependencyFunctionInfoMap);
    }
    initializeTreeData() {
        if (!this.initState.isInInitialState) {
            return;
        }
        this.initState.inProgress();
        this.initializeFunctionInfoMap();
        this.initializeDisplayablePackagesSet()
            .catch(noop())
            .finally(() => {
            this.setTreeData(getFunctionsExplorerTreeData([this._functionGraph.root], this.queryBuilderState, ROOT_PACKAGE_NAME.MAIN));
            this.setFunctionExplorerStates(this.functionInfoMap
                ? Array.from(this.functionInfoMap.values()).map((info) => new QueryFunctionExplorerState(this, info))
                : []);
        });
        if (this.dependencyFunctionInfoMap) {
            this.initializeDependencyDisplayablePackagesSet()
                .catch(noop())
                .finally(() => {
                this.setDependencyTreeData(getFunctionsExplorerTreeData([this._functionGraph.root], this.queryBuilderState, ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT));
                this.setDependencyFunctionExplorerStates(this.dependencyFunctionInfoMap
                    ? Array.from(this.dependencyFunctionInfoMap.values()).map((info) => new QueryFunctionExplorerState(this, info))
                    : []);
            });
        }
        this.initState.pass();
    }
}
//# sourceMappingURL=QueryFunctionsExplorerState.js.map