/**
 * 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 { DataProduct, ModelAccessPointGroup, NativeModelExecutionContext, CORE_PURE_PATH, PackageableRuntime, resolveUsableDataProductClasses, LakehouseRuntime, SimpleFunctionExpression, InstanceValue, Multiplicity, extractElementNameFromPath, SUPPORTED_FUNCTIONS, PackageableElementExplicitReference, resolveDataProductExecutionState, RuntimePointer, } from '@finos/legend-graph';
import { QueryBuilderState } from '../../QueryBuilderState.js';
import {} from '@finos/legend-application';
import { renderDataProductQueryBuilderSetupPanelContent } from '../../../components/workflows/DataProductQueryBuilder.js';
import { ActionState, assertErrorThrown, filterByType, guaranteeNonNullable, } from '@finos/legend-shared';
import { action, computed, flow, makeObservable, observable } from 'mobx';
import { DepotEntityWithOrigin, } from '@finos/legend-storage';
import { compareLabelFn } from '@finos/legend-art';
import { QueryBuilderEmbeddedFromExecutionContextState } from '../../QueryBuilderExecutionContextState.js';
export const buildDataProductOption = (value) => ({
    label: value.name,
    value,
});
export const buildExecOptions = (val) => {
    return {
        label: val.key,
        value: val,
    };
};
export const buildModelAccessPointGroupOption = (value) => ({
    label: value.title ?? value.id,
    value,
});
export class DataProductExecutionState {
    queryBuilderState;
    exectionValue;
    constructor(executionState, queryBuilderState) {
        this.queryBuilderState = queryBuilderState;
        this.exectionValue = executionState;
    }
    get selectedOption() {
        return {
            label: this.label,
            value: this.exectionValue,
        };
    }
}
export class NativeModelDataProductExecutionState extends DataProductExecutionState {
    nativeModelAccess;
    constructor(executionState, queryBuilderState) {
        super(executionState, queryBuilderState);
        this.nativeModelAccess = executionState.__owner;
    }
    get label() {
        return this.exectionValue.key;
    }
    get mapping() {
        return this.exectionValue.mapping.value;
    }
    get featuredElements() {
        return this.nativeModelAccess.featuredElements;
    }
}
export class ModelAccessPointDataProductExecutionState extends DataProductExecutionState {
    selectedRuntime;
    constructor(executionState, queryBuilderState) {
        super(executionState, queryBuilderState);
        makeObservable(this, {
            selectedRuntime: observable,
            compatibleRuntimes: computed,
            showRuntimeOptions: computed,
            changeSelectedRuntime: action,
        });
        this.selectedRuntime = this.compatibleRuntimes[0];
    }
    changeSelectedRuntime(val) {
        this.selectedRuntime = val;
        this.queryBuilderState.changeRuntime(val);
    }
    get label() {
        return this.exectionValue.title ?? this.exectionValue.id;
    }
    get mapping() {
        return this.exectionValue.mapping.value;
    }
    get featuredElements() {
        return this.exectionValue.featuredElements;
    }
    get showRuntimeOptions() {
        return this.compatibleRuntimes.length > 1;
    }
    get compatibleRuntimes() {
        return this.queryBuilderState.graphManagerState.usableRuntimes.filter((runtime) => runtime.runtimeValue instanceof LakehouseRuntime);
    }
}
export class DataProductQueryBuilderState extends QueryBuilderState {
    onClassChange;
    onDataProductChange;
    onExecutionContextChange;
    loadDataProductModelState = ActionState.create();
    dataProduct;
    dataProductArtifact;
    executionState;
    entities;
    prioritizeEntityFunc;
    TEMPORARY__setupPanelContentRenderer = () => renderDataProductQueryBuilderSetupPanelContent(this);
    constructor(applicationStore, graphManagerState, workflow, dataProduct, artifact, actionConfig, executionState, prioritizeEntityFunc, onDataProductChange, onExecutionContextChange, onClassChange, config, sourceInfo) {
        super(applicationStore, graphManagerState, workflow, config, sourceInfo);
        makeObservable(this, {
            dataProduct: observable,
            executionState: observable,
            initWithDataProduct: action,
            handleDataProductChange: action,
            setExecutionState: action,
            selectedDataProductOption: computed,
            isProductLinkable: computed,
            modelAccessPointGroups: computed,
            hasModelAccessPointGroups: computed,
            modelAccessPointGroupOptions: computed,
            activeMapping: computed,
            activeFeaturedElements: computed,
            loadEntities: flow,
            entities: observable,
        });
        // for queries against data product, we always want to default to from
        this.executionContextState =
            new QueryBuilderEmbeddedFromExecutionContextState(this);
        this.workflowState.updateActionConfig(actionConfig);
        this.dataProduct = dataProduct;
        this.dataProductArtifact = artifact;
        this.executionState =
            executionState instanceof NativeModelExecutionContext
                ? new NativeModelDataProductExecutionState(executionState, this)
                : new ModelAccessPointDataProductExecutionState(executionState, this);
        this.prioritizeEntityFunc = prioritizeEntityFunc;
        this.onDataProductChange = onDataProductChange;
        this.onExecutionContextChange = onExecutionContextChange;
        this.onClassChange = onClassChange;
    }
    get isProductLinkable() {
        return false;
    }
    handleDataProductChange(val) {
        try {
            this.loadDataProductModelState.inProgress();
            const dataProduct = this.graphManagerState.graph.getDataProduct(val.path);
            this.initWithDataProduct(dataProduct);
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.notificationService.notifyError(`Failed to change Data Product: ${error.message}`);
        }
    }
    initWithDataProduct(dataProduct) {
        try {
            const execValue = resolveDataProductExecutionState(dataProduct);
            this.dataProduct = dataProduct;
            this.executionState =
                execValue instanceof NativeModelExecutionContext
                    ? new NativeModelDataProductExecutionState(execValue, this)
                    : new ModelAccessPointDataProductExecutionState(execValue, this);
            this.changeMapping(this.executionState.mapping);
            if (this.executionState instanceof NativeModelDataProductExecutionState) {
                const runtime = guaranteeNonNullable(this.executionState.exectionValue.runtime, 'runtime unable to be resolved');
                this.changeRuntime(new RuntimePointer(runtime));
            }
            else if (this.executionState instanceof
                ModelAccessPointDataProductExecutionState &&
                this.executionState.selectedRuntime instanceof PackageableRuntime) {
                this.changeRuntime(this.executionState.selectedRuntime);
            }
            const compatibleClasses = resolveUsableDataProductClasses(this.activeFeaturedElements, this.executionState.mapping, this.graphManagerState, undefined);
            // if there is no chosen class or the chosen one is not compatible
            // with the mapping then pick a compatible class if possible
            if (!this.class || !compatibleClasses.includes(this.class)) {
                const possibleNewClass = compatibleClasses[0];
                if (possibleNewClass) {
                    this.changeClass(possibleNewClass);
                }
            }
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.notificationService.notifyError(`Failed to load Data Product: ${error.message}`);
        }
    }
    copyDataProductLinkToClipBoard() {
        if (!this.isProductLinkable) {
            this.applicationStore.notificationService.notifyError('Data Product link is not available.');
        }
    }
    getElementType() {
        return DataProduct;
    }
    setExecutionState(val) {
        this.executionState =
            val instanceof NativeModelExecutionContext
                ? new NativeModelDataProductExecutionState(val, this)
                : new ModelAccessPointDataProductExecutionState(val, this);
    }
    get modelAccessPointGroups() {
        return this.dataProduct.accessPointGroups.filter(filterByType(ModelAccessPointGroup));
    }
    get hasModelAccessPointGroups() {
        return this.modelAccessPointGroups.length > 0;
    }
    get modelAccessPointGroupOptions() {
        return this.modelAccessPointGroups
            .map(buildModelAccessPointGroupOption)
            .sort(compareLabelFn);
    }
    get activeMapping() {
        return this.executionState.mapping;
    }
    get activeFeaturedElements() {
        return this.executionState.featuredElements;
    }
    buildExecutionContextExpression(lambdaFunction) {
        if (this.executionState instanceof ModelAccessPointDataProductExecutionState) {
            // for model access point group with attached ->with(dataProduct)->from(runtime) to execute query
            let precedingExpression = guaranteeNonNullable(lambdaFunction.expressionSequence[0], `Can't build from() expression: preceding expression is not defined`);
            const withFunc = new SimpleFunctionExpression(extractElementNameFromPath(SUPPORTED_FUNCTIONS.WITH));
            const dataProductInstance = new InstanceValue(Multiplicity.ONE, undefined);
            dataProductInstance.values = [
                PackageableElementExplicitReference.create(this.dataProduct),
            ];
            withFunc.parametersValues = [precedingExpression, dataProductInstance];
            precedingExpression = withFunc;
            const fromFunc = new SimpleFunctionExpression(extractElementNameFromPath(SUPPORTED_FUNCTIONS.FROM));
            const runtime = this.executionState.selectedRuntime;
            if (runtime) {
                const runtimeInstance = new InstanceValue(Multiplicity.ONE, undefined);
                runtimeInstance.values = [
                    PackageableElementExplicitReference.create(runtime),
                ];
                fromFunc.parametersValues = [precedingExpression, runtimeInstance];
                lambdaFunction.expressionSequence[0] = fromFunc;
            }
            return lambdaFunction;
        }
        return super.buildExecutionContextExpression(lambdaFunction);
    }
    *loadEntities() {
        this.loadDataProductModelState.inProgress();
        this.entities = this.graphManagerState.graph.allOwnElements
            .filter(filterByType(this.getElementType()))
            .map((element) => this.transformElement(element));
        this.loadDataProductModelState.complete();
    }
    transformElement(element) {
        return new DepotEntityWithOrigin(undefined, element.name, element.path, CORE_PURE_PATH.DATA_PRODUCT);
    }
    get dataProductOptions() {
        const sortedAllOptions = (this.entities ?? [])
            .map(buildDataProductOption)
            .sort(compareLabelFn);
        return this.prioritizeEntityFunc
            ? [
                ...sortedAllOptions.filter((val) => this.prioritizeEntityFunc?.(val.value)),
                ...sortedAllOptions.filter((val) => !this.prioritizeEntityFunc?.(val.value)),
            ]
            : sortedAllOptions;
    }
    get selectedDataProductOption() {
        return {
            label: this.dataProduct.title ?? this.dataProduct.name,
            value: new DepotEntityWithOrigin(undefined, this.dataProduct.name, this.dataProduct.path, CORE_PURE_PATH.DATA_PRODUCT),
        };
    }
    get isSupported() {
        return (this.dataProduct.nativeModelAccess !== undefined ||
            // contains model access point group
            this.dataProduct.accessPointGroups.filter(filterByType(ModelAccessPointGroup)).length > 0);
    }
    // includes model access point group if more than one group
    get execOptions() {
        return (this.dataProduct.nativeModelAccess?.nativeModelExecutionContexts.map(buildExecOptions) ?? []).sort(compareLabelFn);
    }
    async propagateExecutionContextChange(requireReBuildingGraph) {
        const currentMapping = this.executionContextState.mapping;
        const newMapping = this.activeMapping;
        if (newMapping && newMapping !== currentMapping) {
            this.changeMapping(newMapping, {
                keepQueryContent: true,
            });
            const classes = resolveUsableDataProductClasses(this.activeFeaturedElements, newMapping, this.graphManagerState, undefined);
            if (this.class && !classes.includes(this.class)) {
                this.setClass(classes[0]);
            }
        }
    }
}
//# sourceMappingURL=DataProductQueryBuilderState.js.map