import { of } from 'rxjs';
import { delay, map, mergeMap, tap } from 'rxjs/operators';
import { randomExponentialBackoffTimer } from '../../../helper';
import { Mapper } from '../../../mapper';
const MAX_BATCH_WRITE_ITEMS = 25;
export class BatchWriteSingleTableRequest {
    constructor(dynamoRx, modelClazz, tableName) {
        this.dynamoRx = dynamoRx;
        if (modelClazz === null || modelClazz === undefined) {
            throw new Error("please provide the model clazz for the request, won't work otherwise");
        }
        this.modelClazz = modelClazz;
        this.tableName = tableName;
        this.itemsToProcess = [];
    }
    get toKey() {
        if (!this._keyFn) {
            this._keyFn = Mapper.createToKeyFn(this.modelClazz);
        }
        return this._keyFn;
    }
    delete(items) {
        this.itemsToProcess.push(...items.map(item => ({ DeleteRequest: { Key: this.toKey(item) } })));
        return this;
    }
    put(items) {
        this.itemsToProcess.push(...items.map(item => ({ PutRequest: { Item: Mapper.toDb(item, this.modelClazz) } })));
        return this;
    }
    execNextBatch() {
        const batch = this.itemsToProcess.splice(0, MAX_BATCH_WRITE_ITEMS);
        const batchWriteItemInput = {
            RequestItems: {
                [this.tableName]: batch,
            },
        };
        return this.dynamoRx.batchWriteItem(batchWriteItemInput).pipe(tap((batchWriteManyResponse) => {
            if (batchWriteManyResponse.UnprocessedItems && batchWriteManyResponse.UnprocessedItems[this.tableName]) {
                this.itemsToProcess.unshift(...batchWriteManyResponse.UnprocessedItems[this.tableName]);
            }
        }), map((batchWriteManyResponse) => ({
            remainingItems: this.itemsToProcess.length,
            capacityExceeded: !!(batchWriteManyResponse.UnprocessedItems && batchWriteManyResponse.UnprocessedItems[this.tableName]),
            consumedCapacity: batchWriteManyResponse.ConsumedCapacity,
        })));
    }
    /**
     *
     * @param backoffTimer generator for how much timeSlots should be waited before requesting next batch. only used when capacity was exceeded. default randomExponentialBackoffTimer
     * @param throttleTimeSlot defines how long one timeSlot is for throttling, default 1 second
     */
    exec(backoffTimer = randomExponentialBackoffTimer, throttleTimeSlot = 1000) {
        let rBoT = backoffTimer();
        let backoffTime = 0;
        return this.execNextBatch().pipe(mergeMap((r) => {
            if (!r.capacityExceeded) {
                rBoT = backoffTimer();
                backoffTime = 0;
            }
            else {
                backoffTime = rBoT.next().value;
            }
            return of(r).pipe(delay(backoffTime * throttleTimeSlot));
        }), mergeMap((r) => {
            if (r.remainingItems > 0) {
                return this.exec();
            }
            else {
                return of();
            }
        }));
    }
}
//# sourceMappingURL=batch-write-single-table.request.js.map