import * as tslib_1 from "tslib";
import { isSet, toDbOne } from '../../mapper';
import { deepFilter } from './condition-expression-builder';
import { resolveAttributeNames } from './functions/attribute-names.function';
import { uniqAttributeValueName } from './functions/unique-attribute-value-name.function';
/**
 * Will create a condition which can be added to a request using the param object.
 * It will create the expression statement and the attribute names and values.
 *
 * @param {string} attributePath
 * @param {ConditionOperator} operation
 * @param {any[]} values Depending on the operation the amount of values differs
 * @param {string[]} existingValueNames If provided the existing names are used to make sure we have a unique name for the current attributePath
 * @param {Metadata<any>} metadata If provided we use the metadata to define the attribute name and use it to map the given value(s) to attributeValue(s)
 * @returns {Expression}
 */
export function buildUpdateExpression(attributePath, operation, values, existingValueNames, metadata) {
    // TODO LOW:INVESTIGATE is there a use case for undefined desired to be a value
    // metadata rid of undefined values
    values = deepFilter(values, function (value) { return value !== undefined; });
    // TODO check if provided values are valid for given operation
    // validateValues(operation, values)
    // load property metadat if model metadata was provided
    var propertyMetadata;
    if (metadata) {
        propertyMetadata = metadata.forProperty(attributePath);
    }
    /*
     * resolve placeholder and valuePlaceholder names (same as attributePath if it not already exists)
     * myProp -> #myProp for name placeholder and :myProp for value placeholder
     *
     * person[0] -> #person: person
     * person.list[0].age -> #person: person, #attr: attr, #age: age
     * person.age
     */
    var resolvedAttributeNames = resolveAttributeNames(attributePath, propertyMetadata);
    var valuePlaceholder = uniqAttributeValueName(attributePath, existingValueNames);
    /*
     * build the statement
     */
    // const buildFilterFn: any = curryRight()
    return buildDefaultExpression(attributePath, resolvedAttributeNames.placeholder, valuePlaceholder, resolvedAttributeNames.attributeNames, values, existingValueNames, propertyMetadata, operation);
}
function buildDefaultExpression(attributePath, namePlaceholder, valuePlaceholder, attributeNames, values, existingValueNames, propertyMetadata, operator) {
    var statement;
    switch (operator.action) {
        case 'incrementBy':
            statement = namePlaceholder + " = " + namePlaceholder + " + " + valuePlaceholder;
            break;
        case 'decrementBy':
            statement = namePlaceholder + " = " + namePlaceholder + " - " + valuePlaceholder;
            break;
        case 'set':
            statement = namePlaceholder + " = " + valuePlaceholder;
            break;
        case 'appendToList':
            var position = values.length > 1 ? values[values.length - 1] || 'END' : 'END';
            switch (position) {
                case 'END':
                    statement = namePlaceholder + " = list_append(" + namePlaceholder + ", " + valuePlaceholder + ")";
                    break;
                case 'START':
                    statement = namePlaceholder + " = list_append(" + valuePlaceholder + ", " + namePlaceholder + ")";
                    break;
                default:
                    throw new Error("make sure to provide either 'START' or 'END' as value for position argument");
            }
            break;
        case 'remove':
            statement = "" + namePlaceholder;
            break;
        case 'removeFromListAt':
            statement = values.map(function (pos) { return namePlaceholder + "[" + pos + "]"; }).join(', ');
            break;
        case 'add':
            // TODO add validation to make sure expressionAttributeValue to be N(umber) or S(et)
            statement = namePlaceholder + " " + valuePlaceholder;
            // TODO won't work for numbers, is always gonna be mapped to a collection type
            if ((values.length === 1 && Array.isArray(values[0])) || isSet(values[0])) {
                // dealing with arr | set as single argument
            }
            else {
                // dealing with vararg
                values[0] = tslib_1.__spread(values);
            }
            break;
        case 'removeFromSet':
            // TODO add validation to make sure expressionAttributeValue to be S(et)
            statement = namePlaceholder + " " + valuePlaceholder;
            if ((values.length === 1 && Array.isArray(values[0])) || isSet(values[0])) {
                // dealing with arr | set as single argument
            }
            else {
                // dealing with vararg
                values[0] = tslib_1.__spread(values);
            }
            break;
        default:
            throw new Error("no implementation for action " + operator.action);
    }
    var hasValue = !isNoValueAction(operator.action);
    var attributes = {};
    if (hasValue) {
        var attribute = toDbOne(values[0], propertyMetadata);
        if (attribute) {
            attributes[valuePlaceholder] = attribute;
        }
    }
    return {
        type: operator.actionKeyword,
        statement: statement,
        attributeNames: attributeNames,
        attributeValues: attributes,
    };
}
function isNoValueAction(action) {
    return (action === 'remove' ||
        // special cases: values are used in statement instead of expressionValues
        action === 'removeFromListAt');
}
//# sourceMappingURL=update-expression-builder.js.map