/**
 * 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 { makeObservable, observable, action, flow, flowResult } from 'mobx';
import { V1_PureModelContextData, V1_DataProduct, V1_RelationType, V1_RelationTypeColumn, V1_IngestDefinition, V1_entitiesToPureModelContextData, V1_PackageableType, V1_GenericType, } from '@finos/legend-graph';
import { assertErrorThrown, isNonNullable, } from '@finos/legend-shared';
const DATA_PRODUCT_HEADER_ID = '__data_products_header__';
const INGEST_HEADER_ID = '__ingest_definitions_header__';
export class AccessorExplorerTreeNodeData {
    isOpen;
    id;
    label;
    parentId;
    childrenIds;
    isChecked = false;
    constructor(id, label, parentId) {
        makeObservable(this, {
            isChecked: observable,
            setChecked: action,
        });
        this.id = id;
        this.label = label;
        this.parentId = parentId;
    }
    setChecked(val) {
        this.isChecked = val;
    }
}
export class AccessorExplorerTreeDataProductNodeData extends AccessorExplorerTreeNodeData {
    dataProduct;
    constructor(id, dataProduct, parentId) {
        super(id, dataProduct.title ?? '', parentId);
        this.dataProduct = dataProduct;
    }
}
export class AccessorExplorerTreeIngestNodeData extends AccessorExplorerTreeNodeData {
    ingestDefinition;
    constructor(id, ingestDefinition, parentId) {
        super(id, ingestDefinition.name, parentId);
        this.ingestDefinition = ingestDefinition;
    }
}
export class AccessorExplorerTreeHeaderNodeData extends AccessorExplorerTreeNodeData {
}
export class AccessorExplorerTreeAccessPointGroupNodeData extends AccessorExplorerTreeNodeData {
    parentId;
    accessPointGroupId;
    accessPointGroupTitle;
    dataProductPath;
    constructor(id, parentId, accessPointGroupId, accessPointGroupTitle, dataProductPath) {
        super(id, accessPointGroupTitle, parentId);
        this.parentId = parentId;
        this.accessPointGroupId = accessPointGroupId;
        this.accessPointGroupTitle = accessPointGroupTitle;
        this.dataProductPath = dataProductPath;
    }
}
export class AccessorExplorerTreeAccessPointNodeData extends AccessorExplorerTreeNodeData {
    parentId;
    accessPoint;
    dataProductPath;
    constructor(id, parentId, accessPoint, dataProductPath) {
        super(id, accessPoint.id, parentId);
        this.parentId = parentId;
        this.accessPoint = accessPoint;
        this.dataProductPath = dataProductPath;
    }
}
export class AccessorExplorerTreeDatasetNodeData extends AccessorExplorerTreeNodeData {
    parentId;
    dataset;
    constructor(id, parentId, dataset) {
        super(id, dataset.name, parentId);
        this.parentId = parentId;
        this.dataset = dataset;
    }
}
export class AccessorExplorerTreeColumnNodeData extends AccessorExplorerTreeNodeData {
    parentId;
    owner;
    column;
    constructor(id, parentId, owner, column) {
        super(id, column.name, parentId);
        this.parentId = parentId;
        this.owner = owner;
        this.column = column;
    }
}
export class SQLPlaygroundAccessorExplorerState {
    isGeneratingAccessor = false;
    treeData;
    parsedIngestDefinitions = new Map();
    parsedDataProducts = new Map();
    fetchDataProductArtifact;
    artifactCache = new Map();
    entities;
    plugins;
    constructor(entities, plugins, fetchDataProductArtifact) {
        makeObservable(this, {
            isGeneratingAccessor: observable,
            treeData: observable,
            parsedIngestDefinitions: observable,
            parsedDataProducts: observable,
            artifactCache: observable,
            setTreeData: action,
            parsePMCD: action,
            onNodeSelect: flow,
            fetchProjectData: flow,
            fetchAccessPointGroupMetadata: flow,
            fetchDataProductMetadata: flow,
            fetchIngestMetadata: flow,
            fetchAccessPointMetadata: flow,
            fetchDatasetMetadata: flow,
        });
        this.entities = entities;
        this.plugins = plugins;
        this.fetchDataProductArtifact = fetchDataProductArtifact;
    }
    setTreeData(builderTreeData) {
        this.treeData = builderTreeData;
    }
    parsePMCD(pmcd) {
        this.parsedIngestDefinitions.clear();
        this.parsedDataProducts.clear();
        pmcd.elements.forEach((element) => {
            if (element instanceof V1_IngestDefinition) {
                this.parsedIngestDefinitions.set(element.path, element);
            }
            else if (element instanceof V1_DataProduct) {
                this.parsedDataProducts.set(element.path, element);
            }
        });
    }
    getIngestDefinitionEntries() {
        return Array.from(this.parsedIngestDefinitions.entries());
    }
    getDataProducts() {
        return Array.from(this.parsedDataProducts.values());
    }
    *fetchProjectData() {
        try {
            this.isGeneratingAccessor = true;
            const pmcd = new V1_PureModelContextData();
            yield V1_entitiesToPureModelContextData(this.entities, pmcd, this.plugins);
            this.parsePMCD(pmcd);
            const rootIds = [];
            const nodes = new Map();
            const dataProducts = this.getDataProducts();
            if (dataProducts.length > 0) {
                rootIds.push(DATA_PRODUCT_HEADER_ID);
                const dataProductHeaderNode = new AccessorExplorerTreeHeaderNodeData(DATA_PRODUCT_HEADER_ID, 'Data Products');
                nodes.set(DATA_PRODUCT_HEADER_ID, dataProductHeaderNode);
                const dataProductChildIds = [];
                dataProducts.forEach((dataProduct) => {
                    const dpId = dataProduct.path;
                    const dpNode = new AccessorExplorerTreeDataProductNodeData(dpId, dataProduct, DATA_PRODUCT_HEADER_ID);
                    nodes.set(dpId, dpNode);
                    dataProductChildIds.push(dpId);
                });
                dataProductHeaderNode.childrenIds = dataProductChildIds;
            }
            const ingestEntries = this.getIngestDefinitionEntries();
            if (ingestEntries.length > 0) {
                rootIds.push(INGEST_HEADER_ID);
                const ingestHeaderNode = new AccessorExplorerTreeHeaderNodeData(INGEST_HEADER_ID, 'Ingest Definitions');
                nodes.set(INGEST_HEADER_ID, ingestHeaderNode);
                const ingestChildIds = [];
                ingestEntries.forEach(([ingestPath, ingest]) => {
                    const ingestNode = new AccessorExplorerTreeIngestNodeData(ingestPath, ingest, INGEST_HEADER_ID);
                    nodes.set(ingestPath, ingestNode);
                    ingestChildIds.push(ingestPath);
                });
                ingestHeaderNode.childrenIds = ingestChildIds;
            }
            const treeData = { rootIds, nodes };
            this.setTreeData(treeData);
        }
        catch (error) {
            assertErrorThrown(error);
            throw error;
        }
        finally {
            this.isGeneratingAccessor = false;
        }
    }
    *onNodeSelect(node, treeData) {
        if (!node.childrenIds) {
            if (node instanceof AccessorExplorerTreeDataProductNodeData) {
                yield flowResult(this.fetchDataProductMetadata(node, treeData));
            }
            else if (node instanceof AccessorExplorerTreeIngestNodeData) {
                yield flowResult(this.fetchIngestMetadata(node, treeData));
            }
            else if (node instanceof AccessorExplorerTreeAccessPointGroupNodeData) {
                yield flowResult(this.fetchAccessPointGroupMetadata(node, treeData));
            }
            else if (node instanceof AccessorExplorerTreeAccessPointNodeData) {
                yield flowResult(this.fetchAccessPointMetadata(node, treeData));
            }
            else if (node instanceof AccessorExplorerTreeDatasetNodeData) {
                yield flowResult(this.fetchDatasetMetadata(node, treeData));
            }
        }
        node.isOpen = !node.isOpen;
        this.setTreeData({ ...treeData });
    }
    *fetchDataProductMetadata(node, treeData) {
        yield* this.fetchChildNodes(node, treeData, (n) => this.getDataProductChildren(n));
    }
    *fetchIngestMetadata(node, treeData) {
        yield* this.fetchChildNodes(node, treeData, (n) => this.getIngestChildren(n));
    }
    *fetchAccessPointGroupMetadata(node, treeData) {
        yield* this.fetchChildNodes(node, treeData, (n) => this.getAccessPointGroupChildren(n));
    }
    *fetchAccessPointMetadata(node, treeData) {
        const dataProductPath = node.dataProductPath;
        if (dataProductPath && !this.artifactCache.has(dataProductPath)) {
            try {
                this.isGeneratingAccessor = true;
                const artifact = (yield this.fetchDataProductArtifact(dataProductPath));
                if (artifact) {
                    this.artifactCache.set(dataProductPath, artifact);
                }
            }
            catch (error) {
                assertErrorThrown(error);
                throw error;
            }
            finally {
                this.isGeneratingAccessor = false;
            }
        }
        yield* this.fetchChildNodes(node, treeData, (n) => this.getAccessPointChildren(n));
    }
    *fetchDatasetMetadata(node, treeData) {
        yield* this.fetchChildNodes(node, treeData, (n) => this.getDatasetChildren(n));
    }
    *fetchChildNodes(node, treeData, getChildren) {
        try {
            node.childrenIds?.forEach((childId) => treeData.nodes.delete(childId));
            node.childrenIds = undefined;
            const children = getChildren(node);
            const rootIds = [];
            children.forEach((child) => {
                rootIds.push(child.id);
                treeData.nodes.set(child.id, child);
            });
            node.childrenIds = rootIds;
            this.setTreeData(treeData);
        }
        catch (error) {
            assertErrorThrown(error);
        }
    }
    getDataProductChildren(node) {
        const dataProduct = this.parsedDataProducts.get(node.id);
        if (!dataProduct?.accessPointGroups) {
            return [];
        }
        return dataProduct.accessPointGroups.map((apg) => {
            const apgId = `${node.id}.${apg.id}`;
            return new AccessorExplorerTreeAccessPointGroupNodeData(apgId, node.id, apg.id, apg.title ?? apg.id, node.id);
        });
    }
    getAccessPointGroupChildren(node) {
        const dataProductPath = node.dataProductPath;
        if (!dataProductPath) {
            return [];
        }
        const dataProduct = this.parsedDataProducts.get(dataProductPath);
        const accessPointGroup = dataProduct?.accessPointGroups.find((apg) => apg.id === node.accessPointGroupId);
        if (!accessPointGroup?.accessPoints) {
            return [];
        }
        return accessPointGroup.accessPoints.map((ap) => new AccessorExplorerTreeAccessPointNodeData(ap.id, node.id, ap, dataProductPath));
    }
    getIngestChildren(node) {
        const content = node.ingestDefinition.content;
        const datasets = content.datasets ?? [];
        return datasets.map((dataset) => {
            const datasetId = `${node.id}::${dataset.name}`;
            return new AccessorExplorerTreeDatasetNodeData(datasetId, node.id, dataset);
        });
    }
    getAccessPointChildren(node) {
        const dataProductPath = node.dataProductPath;
        if (!dataProductPath || !this.parsedDataProducts.get(dataProductPath)) {
            return [];
        }
        const dataProductArtifact = this.artifactCache.get(dataProductPath);
        if (!dataProductArtifact) {
            return [];
        }
        const accessPointImplementation = dataProductArtifact.accessPointGroups
            .flatMap((apg) => apg.accessPointImplementations)
            .find((apImpl) => apImpl.id === node.accessPoint.id);
        if (!accessPointImplementation?.lambdaGenericType) {
            return [];
        }
        const relationType = accessPointImplementation.lambdaGenericType.typeArguments
            .map((typeArg) => typeArg.rawType)
            .find((rawType) => rawType instanceof V1_RelationType);
        if (!relationType?.columns) {
            return [];
        }
        return relationType.columns
            .sort((colA, colB) => colA.name.localeCompare(colB.name))
            .map((col) => {
            const colId = `${node.id}::${col.name}`;
            return new AccessorExplorerTreeColumnNodeData(colId, node.id, node.accessPoint, col);
        });
    }
    getDatasetChildren(node) {
        const columns = node.dataset.source.schema.columns;
        if (columns.length === 0) {
            return [];
        }
        return columns
            .sort((colA, colB) => colA.name.localeCompare(colB.name))
            .map((col) => {
            const colId = `${node.id}::${col.name}`;
            const packageableType = new V1_PackageableType();
            packageableType.fullPath = col.genericType.rawType.fullPath;
            const genericType = new V1_GenericType();
            genericType.rawType = packageableType;
            const relationColumn = new V1_RelationTypeColumn();
            relationColumn.name = col.name;
            relationColumn.genericType = genericType;
            return new AccessorExplorerTreeColumnNodeData(colId, node.id, node.dataset, relationColumn);
        });
    }
    getChildNodes(node, treeData) {
        return node.childrenIds
            ?.map((childNode) => treeData.nodes.get(childNode))
            .filter(isNonNullable);
    }
    toggleCheckedNode(node, treeData) {
        node.setChecked(!node.isChecked);
        if (node instanceof AccessorExplorerTreeDataProductNodeData) {
            this.getChildNodes(node, treeData)?.forEach((childNode) => {
                childNode.setChecked(node.isChecked);
            });
        }
        else if (node instanceof AccessorExplorerTreeAccessPointNodeData ||
            node instanceof AccessorExplorerTreeColumnNodeData) {
            if (node.parentId) {
                const parent = treeData.nodes.get(node.parentId);
                if (parent &&
                    this.getChildNodes(parent, treeData)?.every((e) => e.isChecked === node.isChecked)) {
                    parent.setChecked(node.isChecked);
                }
            }
        }
        this.setTreeData({ ...treeData });
    }
}
//# sourceMappingURL=SqlPlaygroundAccessorExplorerState.js.map