import { __awaiter, __generator } from "tslib";
import unfetch from 'unfetch';
var fetch = unfetch;
if (typeof window !== 'undefined') {
    fetch = window.fetch || unfetch;
}
var MAX_PAYLOAD_SIZE = 500;
function kilobytes(buffer) {
    var size = encodeURI(JSON.stringify(buffer)).split(/%..|./).length - 1;
    return size / 1024;
}
/**
 * Checks if the payload is over or close to
 * the maximum payload size allowed by tracking
 * API.
 */
function approachingTrackingAPILimit(buffer) {
    return kilobytes(buffer) >= MAX_PAYLOAD_SIZE - 50;
}
function chunks(batch) {
    var result = [];
    var index = 0;
    batch.forEach(function (item) {
        var size = kilobytes(result[index]);
        if (size >= 64) {
            index++;
        }
        if (result[index]) {
            result[index].push(item);
        }
        else {
            result[index] = [item];
        }
    });
    return result;
}
export default function batch(apiHost, config) {
    var _this = this;
    var _a, _b;
    var buffer = [];
    var flushing = false;
    var limit = (_a = config === null || config === void 0 ? void 0 : config.size) !== null && _a !== void 0 ? _a : 10;
    var timeout = (_b = config === null || config === void 0 ? void 0 : config.timeout) !== null && _b !== void 0 ? _b : 5000;
    function flush() {
        var _a;
        if (flushing) {
            return;
        }
        flushing = true;
        var remote = "https://" + apiHost + "/batch";
        var batch = buffer.map(function (_a) {
            var _url = _a[0], blob = _a[1];
            return blob;
        });
        buffer = [];
        flushing = false;
        var writeKey = (_a = batch[0]) === null || _a === void 0 ? void 0 : _a.writeKey;
        var authtoken = btoa(writeKey + ':');
        return fetch(remote, {
            headers: {
                'Content-Type': 'application/json',
                Authorization: "Basic " + authtoken,
            },
            method: 'post',
            body: JSON.stringify({ batch: batch }),
        });
    }
    // eslint-disable-next-line @typescript-eslint/no-use-before-define
    var schedule = scheduleFlush();
    function scheduleFlush() {
        return setTimeout(function () {
            schedule = undefined;
            if (buffer.length && !flushing) {
                flush();
            }
        }, timeout);
    }
    window.addEventListener('beforeunload', function () {
        if (buffer.length === 0) {
            return;
        }
        var batch = buffer.map(function (_a) {
            var _url = _a[0], blob = _a[1];
            return blob;
        });
        var chunked = chunks(batch);
        var reqs = chunked.map(function (chunk) { return __awaiter(_this, void 0, void 0, function () {
            var remote, writeKey, authtoken;
            var _a;
            return __generator(this, function (_b) {
                if (chunk.length === 0) {
                    return [2 /*return*/];
                }
                remote = "https://" + apiHost + "/batch";
                writeKey = (_a = chunk[0]) === null || _a === void 0 ? void 0 : _a.writeKey;
                authtoken = btoa(writeKey + ':');
                return [2 /*return*/, fetch(remote, {
                        keepalive: true,
                        headers: {
                            'Content-Type': 'application/json',
                            Authorization: "Basic " + authtoken,
                        },
                        method: 'post',
                        body: JSON.stringify({ batch: chunk }),
                    })];
            });
        }); });
        Promise.all(reqs).catch(console.error);
    });
    function dispatch(url, body) {
        return __awaiter(this, void 0, void 0, function () {
            var bufferOverflow;
            return __generator(this, function (_a) {
                buffer.push([url, body]);
                bufferOverflow = buffer.length >= limit || approachingTrackingAPILimit(buffer);
                if (bufferOverflow && !flushing) {
                    flush();
                }
                else {
                    if (!schedule) {
                        schedule = scheduleFlush();
                    }
                }
                return [2 /*return*/, true];
            });
        });
    }
    return {
        dispatch: dispatch,
    };
}
//# sourceMappingURL=batched-dispatcher.js.map