import { hasGenericType } from '../../decorator';
import { notNull } from '../../helper';
import { fromDb, fromDbOne, toDb, toDbOne } from '../mapper';
import { detectCollectionType, isBufferType, isSet } from '../util';
function collectionFromDb(attributeValue, propertyMetadata) {
    const explicitType = propertyMetadata && propertyMetadata.typeInfo ? propertyMetadata.typeInfo.type : null;
    if ('SS' in attributeValue) {
        const arr = attributeValue.SS;
        return explicitType && explicitType === Array ? arr : new Set(arr);
    }
    if ('NS' in attributeValue) {
        const arr = attributeValue.NS.map(parseFloat);
        return explicitType && explicitType === Array ? arr : new Set(arr);
    }
    if ('BS' in attributeValue) {
        const arr = attributeValue.BS;
        return explicitType && explicitType === Array ? arr : new Set(arr);
    }
    if ('L' in attributeValue) {
        let arr;
        if (hasGenericType(propertyMetadata)) {
            arr = attributeValue.L.map(item => fromDb(item.M, propertyMetadata.typeInfo.genericType));
        }
        else {
            // tslint:disable-next-line:no-unnecessary-callback-wrapper
            arr = attributeValue.L.map(v => fromDbOne(v));
        }
        return explicitType && explicitType === Set ? new Set(arr) : arr;
    }
    throw new Error('No Collection Data (SS | NS | BS | L) was found in attribute data');
}
function collectionToDb(propertyValue, propertyMetadata) {
    if (Array.isArray(propertyValue) || isSet(propertyValue)) {
        let collectionType;
        if (propertyMetadata) {
            const explicitType = propertyMetadata && propertyMetadata.typeInfo ? propertyMetadata.typeInfo.type : null;
            switch (explicitType) {
                case Array:
                    collectionType = 'L';
                    break;
                case Set:
                    if (propertyMetadata.isSortedCollection) {
                        // only the L(ist) type preserves order
                        collectionType = 'L';
                    }
                    else {
                        if (propertyValue.size === 0) {
                            // if the type is Set defined by propertyMetadata and the value is empty return null type, empty set is not allowed
                            return { NULL: true };
                        }
                        else {
                            if (hasGenericType(propertyMetadata)) {
                                // generic type of Set is defined, so decide based on the generic type which db set type should be used
                                if (isBufferType(propertyMetadata.typeInfo.genericType)) {
                                    collectionType = 'BS';
                                }
                                else {
                                    switch (propertyMetadata.typeInfo.genericType) {
                                        case String:
                                            collectionType = 'SS';
                                            break;
                                        case Number:
                                            collectionType = 'NS';
                                            break;
                                        default:
                                            // fallback to list if the type is not one of String or Number
                                            collectionType = 'L';
                                    }
                                }
                            }
                            else {
                                // auto detect based on set item values
                                collectionType = detectCollectionType(propertyValue);
                            }
                        }
                    }
                    break;
                default:
                    throw new Error(`only 'Array' and 'Set' are valid values for explicit type, found ${explicitType}`);
            }
        }
        else {
            // auto detect based on set item values
            collectionType = detectCollectionType(propertyValue);
        }
        // convert to array if we deal with a set
        if (isSet(propertyValue)) {
            propertyValue = Array.from(propertyValue);
        }
        // empty value is not allowed for S(et) (supported for L(ist))
        if (propertyValue.length === 0) {
            return null;
        }
        else {
            switch (collectionType) {
                case 'SS':
                    return { SS: propertyValue };
                case 'NS':
                    return { NS: propertyValue.map(num => num.toString()) };
                case 'BS':
                    return { BS: propertyValue };
                case 'L':
                    if (hasGenericType(propertyMetadata)) {
                        return {
                            L: propertyValue.map(value => ({
                                M: toDb(value, propertyMetadata.typeInfo.genericType),
                            })),
                        };
                    }
                    else {
                        return {
                            L: propertyValue
                                // tslint:disable-next-line:no-unnecessary-callback-wrapper
                                .map(v => toDbOne(v))
                                .filter(notNull),
                        };
                    }
                default:
                    throw new Error(`Collection type must be one of SS | NS | BS | L found type ${collectionType}`);
            }
        }
    }
    else {
        throw new Error(`given value is not an array ${propertyValue}`);
    }
}
export const CollectionMapper = {
    fromDb: collectionFromDb,
    toDb: collectionToDb,
};
//# sourceMappingURL=collection.mapper.js.map