/**
 * 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 { assertErrorThrown, LogEvent, addUniqueEntry, guaranteeNonNullable, isNonNullable, filterByType, ActionState, at, guaranteeType, assertNonEmptyString, assertTrue, UnsupportedOperationError, } from '@finos/legend-shared';
import { observable, action, makeObservable, flow, flowResult, computed, } from 'mobx';
import { LEGEND_STUDIO_APP_EVENT } from '../../../../../__lib__/LegendStudioEvent.js';
import { TDSExecutionResult, getColumn, PrimitiveType, PRIMITIVE_TYPE, TDSRow, Table, RelationalDatabaseConnection, DatabaseBuilderInput, DatabasePattern, TargetDatabase, Column, Database, resolvePackagePathAndElementName, getSchema, getNullableSchema, getNullableTable, isStubbed_PackageableElement, isValidFullPath, PackageableElementExplicitReference, getTable, Mapping, EngineRuntime, StoreConnections, IdentifiedConnection, getOrCreateGraphPackage, extractElementNameFromPath, extractPackagePathFromPath, } from '@finos/legend-graph';
import { GraphEditFormModeState } from '../../../GraphEditFormModeState.js';
import { connection_setStore } from '../../../../graph-modifier/DSL_Mapping_GraphModifierHelper.js';
import { getTDSColumnDerivedProperyFromType } from '@finos/legend-query-builder';
import { getPrimitiveTypeFromRelationalType } from '../../../utils/MockDataUtils.js';
const GENERATED_PACKAGE = 'generated';
const TDS_LIMIT = 1000;
const buildTableToTDSQueryGrammar = (table) => {
    const tableName = table.name;
    const schemaName = table.schema.name;
    const db = table.schema._OWNER.path;
    return `|${db}->tableReference(
    '${schemaName}',
    '${tableName}'
  )->tableToTDS()->take(${TDS_LIMIT})`;
};
const buildTableToTDSQueryNonNumericWithColumnGrammar = (column) => {
    const table = guaranteeType(column.owner, Table);
    const tableName = table.name;
    const colName = column.name;
    const schemaName = table.schema.name;
    const db = table.schema._OWNER.path;
    const PREVIEW_COLUMN_NAME = 'Count Value';
    const columnGetter = getTDSColumnDerivedProperyFromType(getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING);
    return `|${db}->tableReference(
    '${schemaName}',
    '${tableName}'
  )->tableToTDS()->restrict(
    ['${colName}']
  )->groupBy(
    ['${colName}'],
    '${PREVIEW_COLUMN_NAME}'->agg(
      row|$row.${columnGetter}('${colName}'),
      y|$y->count()
    )
  )->sort(
    [
      desc('${colName}'),
      asc('${PREVIEW_COLUMN_NAME}')
    ]
  )->take(${TDS_LIMIT})`;
};
const buildTableToTDSQueryNumericWithColumnGrammar = (column) => {
    const table = guaranteeType(column.owner, Table);
    const tableName = table.name;
    const colName = column.name;
    const schemaName = table.schema.name;
    const db = table.schema._OWNER.path;
    const columnGetter = getTDSColumnDerivedProperyFromType(getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING);
    return `|${db}->tableReference(
    '${schemaName}',
    '${tableName}'
  )->tableToTDS()->restrict(
    ['${colName}']
  )->groupBy(
    [],
    [
      'Count'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->count()
    ),
      'Distinct Count'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->distinct()->count()
    ),
      'Sum'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->sum()
    ),
      'Min'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->min()
    ),
      'Max'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->max()
    ),
      'Average'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->average()
    ),
      'Std Dev (Population)'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->stdDevPopulation()
    ),
      'Std Dev (Sample)'->agg(
      row|$row.${columnGetter}('${colName}'),
      x|$x->stdDevSample()
    )
    ]
  )`;
};
const buildTableToTDSQueryColumnQuery = (column) => {
    const type = getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING;
    const numerics = [
        PRIMITIVE_TYPE.NUMBER,
        PRIMITIVE_TYPE.INTEGER,
        PRIMITIVE_TYPE.DECIMAL,
        PRIMITIVE_TYPE.FLOAT,
    ];
    if (numerics.includes(type.path)) {
        return [buildTableToTDSQueryNumericWithColumnGrammar(column), true];
    }
    return [buildTableToTDSQueryNonNumericWithColumnGrammar(column), false];
};
// 1. mapping
// 2. connection
// 3. runtime
const buildTDSModel = (graph, connection, db) => {
    // mapping
    const mappingName = 'EmptyMapping';
    const _mapping = new Mapping(mappingName);
    graph.addElement(_mapping, GENERATED_PACKAGE);
    const engineRuntime = new EngineRuntime();
    engineRuntime.mappings = [
        PackageableElementExplicitReference.create(_mapping),
    ];
    const _storeConnection = new StoreConnections(PackageableElementExplicitReference.create(db));
    // copy over new connection
    const newconnection = new RelationalDatabaseConnection(PackageableElementExplicitReference.create(db), connection.type, connection.datasourceSpecification, connection.authenticationStrategy);
    newconnection.localMode = connection.localMode;
    newconnection.timeZone = connection.timeZone;
    _storeConnection.storeConnections = [
        new IdentifiedConnection('connection1', newconnection),
    ];
    engineRuntime.connections = [_storeConnection];
    return {
        runtime: engineRuntime,
        mapping: _mapping,
    };
};
export class DatabaseSchemaExplorerTreeNodeData {
    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 DatabaseSchemaExplorerTreeSchemaNodeData extends DatabaseSchemaExplorerTreeNodeData {
    schema;
    constructor(id, schema) {
        super(id, schema.name, undefined);
        this.schema = schema;
    }
}
export class DatabaseSchemaExplorerTreeTableNodeData extends DatabaseSchemaExplorerTreeNodeData {
    parentId;
    owner;
    table;
    constructor(id, parentId, owner, table) {
        super(id, table.name, parentId);
        this.parentId = parentId;
        this.owner = owner;
        this.table = table;
    }
}
export class DatabaseSchemaExplorerTreeTabularFunctionNodeData extends DatabaseSchemaExplorerTreeNodeData {
    parentId;
    owner;
    tabularFunction;
    constructor(id, parentId, owner, tabularFunction) {
        super(id, tabularFunction.name, parentId);
        this.parentId = parentId;
        this.owner = owner;
        this.tabularFunction = tabularFunction;
    }
}
export class DatabaseSchemaExplorerTreeColumnNodeData extends DatabaseSchemaExplorerTreeNodeData {
    parentId;
    owner;
    column;
    constructor(id, parentId, owner, column) {
        super(id, column.name, parentId);
        this.parentId = parentId;
        this.owner = owner;
        this.column = column;
    }
}
export const DEFAULT_DATABASE_PATH = 'store::MyDatabase';
export class DatabaseSchemaExplorerState {
    editorStore;
    connection;
    database;
    targetDatabasePath;
    makeTargetDatabasePathEditable;
    isGeneratingDatabase = false;
    isUpdatingDatabase = false;
    treeData;
    previewer;
    previewDataState = ActionState.create();
    constructor(editorStore, connection) {
        makeObservable(this, {
            isGeneratingDatabase: observable,
            isUpdatingDatabase: observable,
            database: observable,
            treeData: observable,
            targetDatabasePath: observable,
            previewer: observable,
            previewDataState: observable,
            makeTargetDatabasePathEditable: observable,
            isCreatingNewDatabase: computed,
            resolveDatabasePackageAndName: computed,
            setTreeData: action,
            setTargetDatabasePath: action,
            setMakeTargetDatabasePathEditable: action,
            onNodeSelect: flow,
            fetchDatabaseMetadata: flow,
            fetchSchemaMetadata: flow,
            fetchTableMetadata: flow,
            fetchTabularFunctionMetadata: flow,
            generateDatabase: flow,
            updateDatabase: flow,
            updateDatabaseAndGraph: flow,
            previewData: flow,
        });
        this.connection = connection;
        this.database = guaranteeType(connection.store?.value, Database);
        this.editorStore = editorStore;
        this.targetDatabasePath = DEFAULT_DATABASE_PATH;
    }
    get isCreatingNewDatabase() {
        if (!this.connection.store) {
            return false;
        }
        return isStubbed_PackageableElement(this.connection.store.value);
    }
    setMakeTargetDatabasePathEditable(val) {
        this.makeTargetDatabasePathEditable = val;
    }
    get resolveDatabasePackageAndName() {
        if (!this.isCreatingNewDatabase && !this.makeTargetDatabasePathEditable) {
            return [
                guaranteeNonNullable(this.database.package).path,
                this.database.name,
            ];
        }
        assertNonEmptyString(this.targetDatabasePath, 'Must specify database path');
        assertTrue(isValidFullPath(this.targetDatabasePath), 'Invalid database path');
        return resolvePackagePathAndElementName(this.targetDatabasePath, this.targetDatabasePath);
    }
    setTargetDatabasePath(val) {
        this.targetDatabasePath = val;
    }
    setTreeData(builderTreeData) {
        this.treeData = builderTreeData;
    }
    *onNodeSelect(node, treeData) {
        if (node instanceof DatabaseSchemaExplorerTreeSchemaNodeData &&
            !node.childrenIds) {
            yield flowResult(this.fetchSchemaMetadata(node, treeData));
        }
        else if (node instanceof DatabaseSchemaExplorerTreeTableNodeData &&
            !node.childrenIds) {
            yield flowResult(this.fetchTableMetadata(node, treeData));
        }
        else if (node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData &&
            !node.childrenIds) {
            yield flowResult(this.fetchTabularFunctionMetadata(node, treeData));
        }
        node.isOpen = !node.isOpen;
        this.setTreeData({ ...treeData });
    }
    getChildNodes(node, treeData) {
        return node.childrenIds
            ?.map((childNode) => treeData.nodes.get(childNode))
            .filter(isNonNullable);
    }
    toggleCheckedNode(node, treeData) {
        node.setChecked(!node.isChecked);
        if (node instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
            this.getChildNodes(node, treeData)?.forEach((childNode) => {
                childNode.setChecked(node.isChecked);
            });
        }
        else if (node instanceof DatabaseSchemaExplorerTreeTableNodeData ||
            node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData) {
            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);
                }
            }
        }
        // TODO: support toggling check for columns
        this.setTreeData({ ...treeData });
    }
    *fetchDatabaseMetadata() {
        try {
            this.isGeneratingDatabase = true;
            const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
            const [packagePath, name] = this.resolveDatabasePackageAndName;
            databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
            databaseBuilderInput.config.maxTables = undefined;
            databaseBuilderInput.config.enrichTables = false;
            databaseBuilderInput.config.enrichTableFunctions = false;
            databaseBuilderInput.config.patterns = [
                new DatabasePattern(undefined, undefined, undefined),
            ];
            const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
            const rootIds = [];
            const nodes = new Map();
            database.schemas
                .toSorted((schemaA, schemaB) => schemaA.name.localeCompare(schemaB.name))
                .forEach((schema) => {
                const schemaId = schema.name;
                rootIds.push(schemaId);
                const schemaNode = new DatabaseSchemaExplorerTreeSchemaNodeData(schemaId, schema);
                nodes.set(schemaId, schemaNode);
                schemaNode.setChecked(Boolean(this.database.schemas.find((cSchema) => cSchema.name === schema.name)));
            });
            const treeData = { rootIds, nodes, database };
            this.setTreeData(treeData);
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.isGeneratingDatabase = false;
        }
    }
    *fetchSchemaMetadata(schemaNode, treeData) {
        try {
            this.isGeneratingDatabase = true;
            const schema = schemaNode.schema;
            const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
            const [packagePath, name] = this.resolveDatabasePackageAndName;
            databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
            databaseBuilderInput.config.maxTables = undefined;
            databaseBuilderInput.config.enrichTables = true;
            databaseBuilderInput.config.enrichTableFunctions = true;
            databaseBuilderInput.config.patterns = [
                new DatabasePattern(schema.name, undefined, undefined),
            ];
            const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
            const tables = getSchema(database, schema.name).tables;
            const tabularFunctions = getSchema(database, schema.name).tabularFunctions;
            const childrenIds = schemaNode.childrenIds ?? [];
            schema.tables = tables;
            schema.tabularFunctions = tabularFunctions;
            tables
                .toSorted((tableA, tableB) => tableA.name.localeCompare(tableB.name))
                .forEach((table) => {
                table.schema = schema;
                const tableId = `${schema.name}.${table.name}`;
                const tableNode = new DatabaseSchemaExplorerTreeTableNodeData(tableId, schemaNode.id, schema, table);
                treeData.nodes.set(tableId, tableNode);
                addUniqueEntry(childrenIds, tableId);
                const matchingSchema = getNullableSchema(this.database, schema.name);
                tableNode.setChecked(Boolean(matchingSchema
                    ? getNullableTable(matchingSchema, table.name)
                    : undefined));
            });
            tabularFunctions
                .toSorted((tabularFunctionA, tabularFunctionB) => tabularFunctionA.name.localeCompare(tabularFunctionB.name))
                .forEach((tabularFunction) => {
                tabularFunction.schema = schema;
                const tabularFunctionId = `${schema.name}.${tabularFunction.name}`;
                const tabularFunctionNode = new DatabaseSchemaExplorerTreeTabularFunctionNodeData(tabularFunctionId, schemaNode.id, schema, tabularFunction);
                treeData.nodes.set(tabularFunctionId, tabularFunctionNode);
                addUniqueEntry(childrenIds, tabularFunctionId);
                const matchingSchema = getNullableSchema(this.database, schema.name);
                tabularFunctionNode.setChecked(Boolean(matchingSchema
                    ? getNullableTable(matchingSchema, tabularFunction.name)
                    : undefined));
            });
            schemaNode.childrenIds = childrenIds;
            this.setTreeData({ ...treeData });
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.isGeneratingDatabase = false;
        }
    }
    *fetchTableMetadata(tableNode, treeData) {
        try {
            this.isGeneratingDatabase = true;
            const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
            const [packagePath, name] = this.resolveDatabasePackageAndName;
            databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
            const table = tableNode.table;
            const config = databaseBuilderInput.config;
            config.maxTables = undefined;
            config.enrichTables = true;
            config.enrichTableFunctions = true;
            config.enrichColumns = true;
            config.enrichPrimaryKeys = true;
            config.patterns = [
                new DatabasePattern(table.schema.name, table.name, undefined),
            ];
            const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
            const enrichedTable = database.schemas
                .find((s) => table.schema.name === s.name)
                ?.tables.find((t) => t.name === table.name);
            if (enrichedTable) {
                table.primaryKey = enrichedTable.primaryKey;
                const columns = enrichedTable.columns.filter(filterByType(Column));
                tableNode.table.columns = columns;
                tableNode.childrenIds?.forEach((childId) => treeData.nodes.delete(childId));
                tableNode.childrenIds = undefined;
                const childrenIds = [];
                const tableId = tableNode.id;
                columns
                    .toSorted((colA, colB) => colA.name.localeCompare(colB.name))
                    .forEach((column) => {
                    const columnId = `${tableId}.${column.name}`;
                    const columnNode = new DatabaseSchemaExplorerTreeColumnNodeData(columnId, tableId, table, column);
                    column.owner = tableNode.table;
                    treeData.nodes.set(columnId, columnNode);
                    addUniqueEntry(childrenIds, columnId);
                });
                tableNode.childrenIds = childrenIds;
            }
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.isGeneratingDatabase = false;
        }
    }
    *fetchTabularFunctionMetadata(tabularFunctionNode, treeData) {
        try {
            this.isGeneratingDatabase = true;
            const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
            const [packagePath, name] = this.resolveDatabasePackageAndName;
            databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
            const tabularFunction = tabularFunctionNode.tabularFunction;
            const config = databaseBuilderInput.config;
            config.maxTables = undefined;
            config.enrichTables = false;
            config.enrichTableFunctions = true;
            config.enrichColumns = true;
            config.enrichPrimaryKeys = true;
            config.patterns = [
                new DatabasePattern(tabularFunction.schema.name, undefined, tabularFunction.name),
            ];
            const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
            const enrichedTabularFunction = database.schemas
                .find((s) => tabularFunction.schema.name === s.name)
                ?.tabularFunctions.find((t) => t.name === tabularFunction.name);
            if (enrichedTabularFunction) {
                const columns = enrichedTabularFunction.columns.filter(filterByType(Column));
                tabularFunctionNode.tabularFunction.columns = columns;
                tabularFunctionNode.childrenIds?.forEach((childId) => treeData.nodes.delete(childId));
                tabularFunctionNode.childrenIds = undefined;
                const childrenIds = [];
                const tabularFunctionId = tabularFunctionNode.id;
                columns
                    .toSorted((colA, colB) => colA.name.localeCompare(colB.name))
                    .forEach((column) => {
                    const columnId = `${tabularFunctionId}.${column.name}`;
                    const columnNode = new DatabaseSchemaExplorerTreeColumnNodeData(columnId, tabularFunctionId, tabularFunction, column);
                    column.owner = tabularFunctionNode.tabularFunction;
                    treeData.nodes.set(columnId, columnNode);
                    addUniqueEntry(childrenIds, columnId);
                });
                tabularFunctionNode.childrenIds = childrenIds;
            }
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.isGeneratingDatabase = false;
        }
    }
    async buildIntermediateDatabase(databaseBuilderInput) {
        const entities = await this.editorStore.graphManagerState.graphManager.buildDatabase(databaseBuilderInput);
        const graph = this.editorStore.graphManagerState.createNewGraph();
        await this.editorStore.graphManagerState.graphManager.buildGraph(graph, entities, ActionState.create());
        return at(graph.ownDatabases, 0, 'Expected one database to be generated from input');
    }
    *previewData(node) {
        try {
            this.previewer = undefined;
            this.previewDataState.inProgress();
            let column;
            let nodeInstance;
            if (node instanceof DatabaseSchemaExplorerTreeTableNodeData) {
                nodeInstance = node.table;
            }
            else if (node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData) {
                nodeInstance = node.tabularFunction;
            }
            else if (node instanceof DatabaseSchemaExplorerTreeColumnNodeData) {
                nodeInstance = guaranteeType(node.column.owner, Table);
                column = node.column;
            }
            else {
                throw new UnsupportedOperationError('Preview data only supported for column, table and tabular function');
            }
            const schemaName = nodeInstance.schema.name;
            const tableName = nodeInstance.name;
            const dummyPackage = 'generation';
            const dummyName = 'myDB';
            const dummyDbPath = `${dummyPackage}::${dummyName}`;
            const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
            databaseBuilderInput.targetDatabase = new TargetDatabase(dummyPackage, dummyName);
            const config = databaseBuilderInput.config;
            config.maxTables = undefined;
            config.enrichColumns = true;
            config.enrichPrimaryKeys = true;
            if (node instanceof DatabaseSchemaExplorerTreeTableNodeData) {
                config.enrichTables = true;
                config.enrichTableFunctions = false;
                config.patterns.push(new DatabasePattern(schemaName, node.table.name, undefined));
            }
            else if (node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData) {
                config.enrichTables = false;
                config.enrichTableFunctions = true;
                config.patterns.push(new DatabasePattern(schemaName, undefined, node.tabularFunction.name));
            }
            const entities = (yield this.editorStore.graphManagerState.graphManager.buildDatabase(databaseBuilderInput));
            assertTrue(entities.length === 1);
            const dbEntity = guaranteeNonNullable(entities[0]);
            const emptyGraph = this.editorStore.graphManagerState.createNewGraph();
            (yield this.editorStore.graphManagerState.graphManager.buildGraph(emptyGraph, [dbEntity], ActionState.create()));
            const generatedDb = emptyGraph.getDatabase(dummyDbPath);
            const resolvedTable = getTable(getSchema(generatedDb, schemaName), tableName);
            let queryGrammar;
            let resolveResult = false;
            if (column) {
                const resolvedColumn = getColumn(resolvedTable, column.name);
                const grammarResult = buildTableToTDSQueryColumnQuery(resolvedColumn);
                queryGrammar = grammarResult[0];
                resolveResult = grammarResult[1];
            }
            else {
                queryGrammar = buildTableToTDSQueryGrammar(resolvedTable);
            }
            const rawLambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(queryGrammar, 'QUERY'));
            const { mapping, runtime } = buildTDSModel(emptyGraph, this.connection, generatedDb);
            const execPlan = (yield this.editorStore.graphManagerState.graphManager.runQuery(rawLambda, mapping, runtime, emptyGraph)).executionResult;
            let tdsResult = guaranteeType(execPlan, TDSExecutionResult, 'Execution from `tabletoTDS` expected to be TDS');
            if (resolveResult) {
                const newResult = new TDSExecutionResult();
                newResult.result.columns = ['Aggregation', 'Value'];
                newResult.result.rows = tdsResult.result.columns.map((col, idx) => {
                    const _row = new TDSRow();
                    _row.values = [
                        col,
                        guaranteeNonNullable(guaranteeNonNullable(tdsResult.result.rows[0]).values[idx]),
                    ];
                    return _row;
                });
                tdsResult = newResult;
            }
            this.previewer = tdsResult;
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.notificationService.notifyError(`Unable to preview data: ${error.message}`);
        }
        finally {
            this.previewDataState.complete();
        }
    }
    *generateDatabase() {
        try {
            this.isGeneratingDatabase = true;
            const treeData = guaranteeNonNullable(this.treeData);
            const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
            const [packagePath, name] = this.resolveDatabasePackageAndName;
            databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
            const config = databaseBuilderInput.config;
            config.maxTables = undefined;
            config.enrichColumns = true;
            config.enrichPrimaryKeys = true;
            config.enrichTables = false;
            config.enrichTableFunctions = false;
            treeData.rootIds
                .map((e) => treeData.nodes.get(e))
                .filter(isNonNullable)
                .forEach((schemaNode) => {
                if (schemaNode instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
                    const nodes = this.getChildNodes(schemaNode, treeData);
                    const allChecked = nodes?.every((t) => t.isChecked === true);
                    if (allChecked ||
                        (schemaNode.isChecked && !schemaNode.childrenIds)) {
                        config.enrichTables = true;
                        config.enrichTableFunctions = true;
                        config.patterns.push(new DatabasePattern(schemaNode.schema.name, undefined, undefined));
                    }
                    else {
                        nodes?.forEach((t) => {
                            if (t instanceof DatabaseSchemaExplorerTreeTableNodeData &&
                                t.isChecked) {
                                config.enrichTables = true;
                                config.patterns.push(new DatabasePattern(schemaNode.schema.name, t.table.name, undefined));
                            }
                            else if (t instanceof
                                DatabaseSchemaExplorerTreeTabularFunctionNodeData &&
                                t.isChecked) {
                                config.enrichTableFunctions = true;
                                config.patterns.push(new DatabasePattern(schemaNode.schema.name, undefined, t.tabularFunction.name));
                            }
                        });
                    }
                }
            });
            const entities = (yield this.editorStore.graphManagerState.graphManager.buildDatabase(databaseBuilderInput));
            return at(entities, 0, 'Expected a database to be generated');
        }
        finally {
            this.isGeneratingDatabase = false;
        }
    }
    // this method just updates database
    *updateDatabase(forceRename) {
        this.isUpdatingDatabase = true;
        const graph = this.editorStore.graphManagerState.createNewGraph();
        (yield this.editorStore.graphManagerState.graphManager.buildGraph(graph, [(yield flowResult(this.generateDatabase()))], ActionState.create()));
        const database = at(graph.ownDatabases, 0, 'Expected one database to be generated from input');
        // remove undefined schemas
        const schemas = Array.from(guaranteeNonNullable(this.treeData).nodes.values())
            .map((schemaNode) => {
            if (schemaNode instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
                return schemaNode.schema;
            }
            return undefined;
        })
            .filter(isNonNullable);
        // update this.database packge and name
        if (forceRename || this.database.name === '' || !this.database.package) {
            this.database.package = getOrCreateGraphPackage(graph, extractPackagePathFromPath(this.targetDatabasePath), undefined);
            this.database.name = extractElementNameFromPath(this.targetDatabasePath);
        }
        // update schemas
        this.database.schemas = this.database.schemas.filter((schema) => {
            if (schemas.find((item) => item.name === schema.name) &&
                !database.schemas.find((s) => s.name === schema.name)) {
                return false;
            }
            return true;
        });
        // update existing schemas
        database.schemas.forEach((schema) => {
            schema._OWNER = this.database;
            const currentSchemaIndex = this.database.schemas.findIndex((item) => item.name === schema.name);
            if (currentSchemaIndex !== -1) {
                this.database.schemas[currentSchemaIndex] = schema;
            }
            else {
                this.database.schemas.push(schema);
            }
        });
        this.isUpdatingDatabase = false;
        return database;
    }
    // this method updates database and add database to the graph
    *updateDatabaseAndGraph() {
        if (!this.treeData) {
            return;
        }
        try {
            const createDatabase = this.isCreatingNewDatabase &&
                !this.editorStore.graphManagerState.graph.databases.includes(this.database);
            this.isUpdatingDatabase = true;
            const database = (yield flowResult(this.updateDatabase()));
            if (createDatabase) {
                connection_setStore(this.connection, PackageableElementExplicitReference.create(database));
                const packagePath = guaranteeNonNullable(database.package?.name, 'Database package is missing');
                yield flowResult(this.editorStore.graphEditorMode.addElement(database, packagePath, false));
            }
            this.editorStore.applicationStore.notificationService.notifySuccess(`Database successfully updated`);
            yield flowResult(this.editorStore
                .getGraphEditorMode(GraphEditFormModeState)
                .globalCompile({
                message: `Can't compile graph after editing database. Redirecting you to text mode`,
            }));
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.isUpdatingDatabase = false;
        }
    }
}
//# sourceMappingURL=DatabaseBuilderState.js.map