/**
 * 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 { ColSpec, ColSpecInstanceValue, CollectionInstanceValue, GenericType, GenericTypeExplicitReference, Multiplicity, PrimitiveInstanceValue, PrimitiveType, SimpleFunctionExpression, createPrimitiveInstance_String, extractElementNameFromPath, matchFunctionName, } from '@finos/legend-graph';
import { COLUMN_SORT_TYPE, QUERY_BUILDER_SUPPORTED_FUNCTIONS, } from '../../../../graph/QueryBuilderMetaModelConst.js';
import { guaranteeNonNullable } from '@finos/legend-shared';
export class ResultModifierValueSpecificationBuilder {
    _currentResultModifierFunc;
    graphManagerState;
    options;
    distinct = false;
    sortColumns;
    limit;
    slice;
    constructor(graphManagerState) {
        this.graphManagerState = graphManagerState;
    }
    get currentExpression() {
        return guaranteeNonNullable(this._currentResultModifierFunc, `Current expression needs to be defined to build result modifier`);
    }
    get supportedResultModifiersFunctions() {
        return [
            QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_PROJECT,
            QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_GROUP_BY,
            QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_FILTER,
            QUERY_BUILDER_SUPPORTED_FUNCTIONS.OLAP_GROUPBY,
        ];
    }
    getSortTypeFunctionName(sortType) {
        return sortType === COLUMN_SORT_TYPE.ASC
            ? this.ascFunctionname
            : this.descFunctionName;
    }
    // builders
    setCurrentResultModifierFunction(val) {
        this._currentResultModifierFunc = val;
    }
    withOptions(options) {
        this.options = options;
        return this;
    }
    withDistinct(distinct) {
        this.distinct = distinct;
        return this;
    }
    withSortColumns(sortCols) {
        if (sortCols?.length) {
            this.sortColumns = sortCols;
        }
        return this;
    }
    withLimit(limit) {
        this.limit = limit;
        return this;
    }
    withSlice(slice) {
        this.slice = slice;
        return this;
    }
    // builder
    buildDistinctFunction() {
        const distinctFunction = new SimpleFunctionExpression(extractElementNameFromPath(this.distinctFunctionName));
        distinctFunction.parametersValues[0] = this.currentExpression;
        this.setCurrentResultModifierFunction(distinctFunction);
    }
    buildSortFunction(sortColumns) {
        const sortFunction = new SimpleFunctionExpression(extractElementNameFromPath(this.sortFunctionName));
        const multiplicity = this.graphManagerState.graph.getMultiplicity(sortColumns.length, sortColumns.length);
        const collection = new CollectionInstanceValue(multiplicity, undefined);
        collection.values = sortColumns.map((e) => this.buildSortExpression(e));
        sortFunction.parametersValues[0] = this.currentExpression;
        sortFunction.parametersValues[1] = collection;
        this.setCurrentResultModifierFunction(sortFunction);
    }
    buildSortExpression(sortColumnState) {
        const sortColumnFunction = new SimpleFunctionExpression(extractElementNameFromPath(this.getSortTypeFunctionName(sortColumnState.sortType)));
        sortColumnFunction.parametersValues[0] = this.buildColumnValueSpec(sortColumnState.columnState.columnName);
        return sortColumnFunction;
    }
    buildLimitFunction(limit) {
        const limitPrimitiveInstanceVal = new PrimitiveInstanceValue(GenericTypeExplicitReference.create(new GenericType(PrimitiveType.INTEGER)));
        limitPrimitiveInstanceVal.values = [
            Math.min(limit
                ? this.options?.withDataOverflowCheck
                    ? limit + 1
                    : limit
                : Number.MAX_SAFE_INTEGER, this.options?.overridingLimit
                ? this.options.withDataOverflowCheck
                    ? this.options.overridingLimit + 1
                    : this.options.overridingLimit
                : Number.MAX_SAFE_INTEGER),
        ];
        const takeFunction = new SimpleFunctionExpression(extractElementNameFromPath(this.limitFunctionName));
        takeFunction.parametersValues[0] = this.currentExpression;
        takeFunction.parametersValues[1] = limitPrimitiveInstanceVal;
        this.setCurrentResultModifierFunction(takeFunction);
    }
    buildSliceFunction(slice) {
        const sliceStart = slice[0];
        const sliceEnd = slice[1];
        const startVal = new PrimitiveInstanceValue(GenericTypeExplicitReference.create(new GenericType(PrimitiveType.INTEGER)));
        const endVal = new PrimitiveInstanceValue(GenericTypeExplicitReference.create(new GenericType(PrimitiveType.INTEGER)));
        startVal.values = [sliceStart];
        endVal.values = [sliceEnd];
        const sliceFunction = new SimpleFunctionExpression(extractElementNameFromPath(this.sliceFunctionName));
        sliceFunction.parametersValues = [this.currentExpression, startVal, endVal];
        this.setCurrentResultModifierFunction(sliceFunction);
    }
    build(lambdaFunction) {
        if (lambdaFunction.expressionSequence.length === 1) {
            const func = lambdaFunction.expressionSequence[0];
            if (func instanceof SimpleFunctionExpression) {
                if (matchFunctionName(func.functionName, this.supportedResultModifiersFunctions)) {
                    this._currentResultModifierFunc = func;
                    // build distinct()
                    if (this.distinct) {
                        this.buildDistinctFunction();
                    }
                    // build sort()
                    if (this.sortColumns) {
                        this.buildSortFunction(this.sortColumns);
                    }
                    // build take()
                    if (this.limit || this.options?.overridingLimit) {
                        this.buildLimitFunction(this.limit);
                    }
                    // build slice()
                    if (this.slice) {
                        this.buildSliceFunction(this.slice);
                    }
                    lambdaFunction.expressionSequence[0] = this.currentExpression;
                    return lambdaFunction;
                }
            }
        }
        return lambdaFunction;
    }
}
export class TDSResultModifierValueSpecificationBuilder extends ResultModifierValueSpecificationBuilder {
    get limitFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_TAKE;
    }
    get sliceFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.SLICE;
    }
    get sortFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_SORT;
    }
    get distinctFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_DISTINCT;
    }
    get ascFunctionname() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_ASC;
    }
    get descFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.TDS_DESC;
    }
    buildColumnValueSpec(columnName) {
        return createPrimitiveInstance_String(columnName);
    }
}
export class TypedResultModifierValueSpecificationBuilder extends ResultModifierValueSpecificationBuilder {
    get limitFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.RELATION_LIMIT;
    }
    get sliceFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.RELATION_SLICE;
    }
    get ascFunctionname() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.RELATION_ASC;
    }
    get descFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.RELATION_DESC;
    }
    get sortFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.RELATION_SORT;
    }
    get distinctFunctionName() {
        return QUERY_BUILDER_SUPPORTED_FUNCTIONS.RELATION_DISTINCT;
    }
    buildColumnValueSpec(columnName) {
        const colSpec = new ColSpecInstanceValue(Multiplicity.ONE, undefined);
        const value = new ColSpec();
        value.name = columnName;
        colSpec.values = [value];
        return colSpec;
    }
}
export const appendResultSetModifier = (resultModifierState, lambdaFunction, isTyped, options) => {
    const builder = isTyped
        ? new TypedResultModifierValueSpecificationBuilder(resultModifierState.tdsState.queryBuilderState.graphManagerState)
        : new TDSResultModifierValueSpecificationBuilder(resultModifierState.tdsState.queryBuilderState.graphManagerState);
    return builder
        .withOptions(options)
        .withDistinct(resultModifierState.distinct)
        .withSortColumns(resultModifierState.sortColumns)
        .withLimit(resultModifierState.limit)
        .withSlice(resultModifierState.slice)
        .build(lambdaFunction);
};
//# sourceMappingURL=ResultModifierValueSpecificationBuilder.js.map