import { curry } from 'lodash';
import { buildFilterExpression } from './condition-expression-builder';
import { addExpression } from './param-util';
import { OPERATOR_TO_ALIAS_MAP } from './type/condition-operator-to-alias-map.const';
import { UPDATE_ACTION_DEFS } from './type/update-action-defs.const';
import { buildUpdateExpression } from './update-expression-builder';
/**
 * Adds a condition to the given query.
 */
export function addCondition(expressionType, attributePath, request, metadata) {
    const f = (operator) => {
        return (...values) => {
            return doAddCondition(expressionType, attributePath, request, metadata, operator, ...values);
        };
    };
    return createConditionFunctions(f);
}
export function addSortKeyCondition(keyName, request, metadata) {
    const f = (operator) => {
        return (...values) => {
            return doAddCondition('KeyConditionExpression', keyName, request, metadata, operator, ...values);
        };
    };
    // only a subset of available operators are supported for sort keys
    return createConditionFunctions(f, '=', '<=', '<', '>', '>=', 'begins_with', 'BETWEEN');
}
export function doAddCondition(expressionType, attributePath, request, metadata, operator, ...values) {
    const copy = [...values];
    const existingValueKeys = request.params.ExpressionAttributeValues
        ? Object.keys(request.params.ExpressionAttributeValues)
        : [];
    const condition = buildFilterExpression(attributePath, operator, copy, existingValueKeys, metadata);
    addExpression(expressionType, condition, request.params);
    return request;
}
export function addPartitionKeyCondition(keyName, keyValue, request, metadata) {
    if (metadata) {
        return addSortKeyCondition(keyName, request, metadata).equals(keyValue);
    }
    else {
        return addSortKeyCondition(keyName, request).equals(keyValue);
    }
}
export function updateDefinitionFunction(attributePath) {
    const f = (operation) => {
        return (...values) => {
            const copy = [...values];
            const curried = curry(buildUpdateExpression);
            return curried(attributePath, operation, copy);
        };
    };
    return createUpdateFunctions(f);
}
export function propertyDefinitionFunction(attributePath) {
    const f = (operator) => {
        return (...values) => {
            const copy = [...values];
            const curried = curry(buildFilterExpression);
            return curried(attributePath, operator, copy);
        };
    };
    return createConditionFunctions(f);
}
/**
 * Creates an object which contains callable functions for all update operations defined in update-operation type
 * for all the values included in operators
 *
 * @param {(operator: ConditionOperator) => any} impl The function which is called with the operator and returns a function which expects the value
 * for the condition. when executed the implementation defines what to do with the condition, just return it for example or add the condition to the request
 * parameters as another example
 */
function createUpdateFunctions(impl) {
    return UPDATE_ACTION_DEFS.reduce((result, updateActionDef) => {
        Reflect.set(result, updateActionDef.action, impl(updateActionDef));
        return result;
    }, {});
}
/**
 * Creates an object which contains callable functions for all aliases defined in CONDITION_OPERATOR_ALIAS or if operators parameter is defined,
 * for all the values included in operators
 *
 * @param {(operator: ConditionOperator) => any} impl The function which is called with the operator and returns a function which expects the value
 * for the condition. when executed the implementation defines what to do with the condition, just return it for example or add the condition to the request
 * parameters as another example
 */
function createConditionFunctions(impl, ...operators) {
    const includedAlias = operators && operators.length ? operators : Object.keys(OPERATOR_TO_ALIAS_MAP);
    return includedAlias.reduce((result, operator) => {
        const alias = OPERATOR_TO_ALIAS_MAP[operator];
        if (Array.isArray(alias)) {
            alias.forEach(a => Reflect.set(result, a, impl(operator)));
        }
        else {
            Reflect.set(result, alias, impl(operator));
        }
        return result;
    }, {});
}
//# sourceMappingURL=request-expression-builder.js.map