import { isObject, isString } from 'lodash';
import { map } from 'rxjs/operators';
import { hasSortKey } from '../../decorator/metadata';
import { metadataForClass } from '../../decorator/metadata/metadata-helper';
import { fromDb, toDbOne } from '../../mapper';
import { DEFAULT_SESSION_VALIDITY_ENSURER } from '../default-session-validity-ensurer.const';
import { DEFAULT_TABLE_NAME_RESOLVER } from '../default-table-name-resolver.const';
import { DynamoRx } from '../dynamo-rx';
import { REGEX_TABLE_NAME } from '../request/regex';
export class BatchGetRequest {
    constructor(tableNameResolver = DEFAULT_TABLE_NAME_RESOLVER, sessionValidityEnsurer = DEFAULT_SESSION_VALIDITY_ENSURER) {
        this.tableNameResolver = tableNameResolver;
        this.tables = new Map();
        this.dynamoRx = new DynamoRx(sessionValidityEnsurer);
        this.params = {
            RequestItems: {},
        };
    }
    /**
     * @param {ModelConstructor<T>} modelClazz
     * @param {any[]} keys either a simple string for partition key or an object with partitionKey and sortKey
     * @returns {BatchGetSingleTableRequest}
     */
    forModel(modelClazz, keys) {
        const tableName = this.getTableName(modelClazz, this.tableNameResolver);
        if (this.tables.has(tableName)) {
            throw new Error('table name already exists, please provide all the keys for the same table at once');
        }
        this.tables.set(tableName, modelClazz);
        const metadata = metadataForClass(modelClazz);
        const attributeMaps = [];
        // loop over all the keys
        keys.forEach(key => {
            const idOb = {};
            if (isString(key)) {
                // got a simple primary key
                const value = toDbOne(key);
                if (value === null) {
                    throw Error('please provide an actual value for partition key');
                }
                idOb[metadata.getPartitionKey()] = value;
            }
            else if (isObject(key) && key.partitionKey !== undefined && key.partitionKey !== null) {
                // got a composite primary key
                // partition key
                const mappedPartitionKey = toDbOne(key.partitionKey);
                if (mappedPartitionKey === null) {
                    throw Error('please provide an actual value for partition key');
                }
                idOb[metadata.getPartitionKey()] = mappedPartitionKey;
                // sort key
                const mappedSortKey = toDbOne(key.sortKey);
                if (mappedSortKey === null) {
                    throw Error('please provide an actual value for partition key');
                }
                if (hasSortKey(metadata)) {
                    idOb[metadata.getSortKey()] = mappedSortKey;
                }
            }
            else {
                throw new Error('a key must either be a string or a PrimaryKey');
            }
            attributeMaps.push(idOb);
        });
        this.params.RequestItems[tableName] = {
            Keys: attributeMaps,
        };
        return this;
    }
    execFullResponse() {
        return this.dynamoRx.batchGetItems(this.params).pipe(map(response => {
            let Responses = {};
            if (response.Responses && Object.keys(response.Responses).length) {
                Responses = Object.entries(response.Responses).reduce((u, [key, val]) => {
                    u[key] = val.map(attributes => fromDb(attributes, this.tables.get(key)));
                    return u;
                }, {});
            }
            return {
                ConsumedCapacity: response.ConsumedCapacity,
                UnprocessedKeys: response.UnprocessedKeys,
                Responses,
            };
        }));
    }
    exec() {
        return this.dynamoRx.batchGetItems(this.params).pipe(map(response => {
            if (response.Responses && Object.keys(response.Responses).length) {
                return Object.entries(response.Responses).reduce((u, [key, val]) => {
                    u[key] = val.map(attributeMap => fromDb(attributeMap, this.tables.get(key)));
                    return u;
                }, {});
            }
            return {};
        }));
    }
    getTableName(modelClazz, tableNameResolver) {
        const tableName = tableNameResolver(metadataForClass(modelClazz).modelOptions.tableName);
        if (!REGEX_TABLE_NAME.test(tableName)) {
            throw new Error('make sure the table name only contains these characters «a-z A-Z 0-9 - _ .» and is between 3 and 255 characters long');
        }
        return tableName;
    }
}
//# sourceMappingURL=batch-get.request.js.map