import { __assign, __awaiter, __generator, __values } from "tslib";
import { BaseTransport, Status } from '@amplitude/analytics-core';
import { getCurrentUrl, getServerUrl } from './helpers';
import { MAX_RETRIES_EXCEEDED_MESSAGE, MISSING_API_KEY_MESSAGE, MISSING_DEVICE_ID_MESSAGE, UNEXPECTED_ERROR_MESSAGE, UNEXPECTED_NETWORK_ERROR_MESSAGE, } from './messages';
import { VERSION } from './version';
import { MAX_URL_LENGTH, KB_SIZE } from './constants';
var SessionReplayTrackDestination = /** @class */ (function () {
    function SessionReplayTrackDestination(_a) {
        var trackServerUrl = _a.trackServerUrl, loggerProvider = _a.loggerProvider, payloadBatcher = _a.payloadBatcher;
        this.storageKey = '';
        this.retryTimeout = 1000;
        this.scheduled = null;
        this.queue = [];
        this.loggerProvider = loggerProvider;
        this.payloadBatcher = payloadBatcher ? payloadBatcher : function (payload) { return payload; };
        this.trackServerUrl = trackServerUrl;
    }
    SessionReplayTrackDestination.prototype.sendEventsList = function (destinationData) {
        this.addToQueue(__assign(__assign({}, destinationData), { attempts: 0, timeout: 0 }));
    };
    SessionReplayTrackDestination.prototype.addToQueue = function () {
        var _this = this;
        var list = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            list[_i] = arguments[_i];
        }
        var tryable = list.filter(function (context) {
            if (context.attempts < (context.flushMaxRetries || 0)) {
                context.attempts += 1;
                return true;
            }
            _this.completeRequest({
                context: context,
                err: MAX_RETRIES_EXCEEDED_MESSAGE,
            });
            return false;
        });
        tryable.forEach(function (context) {
            _this.queue = _this.queue.concat(context);
            _this.schedule(0);
        });
    };
    SessionReplayTrackDestination.prototype.schedule = function (timeout) {
        var _this = this;
        if (this.scheduled)
            return;
        this.scheduled = setTimeout(function () {
            void _this.flush(true).then(function () {
                if (_this.queue.length > 0) {
                    _this.schedule(timeout);
                }
            });
        }, timeout);
    };
    SessionReplayTrackDestination.prototype.flush = function (useRetry) {
        if (useRetry === void 0) { useRetry = false; }
        return __awaiter(this, void 0, void 0, function () {
            var list, list_1, list_1_1, context, e_1_1;
            var e_1, _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        list = this.queue;
                        this.queue = [];
                        if (this.scheduled) {
                            clearTimeout(this.scheduled);
                            this.scheduled = null;
                        }
                        _b.label = 1;
                    case 1:
                        _b.trys.push([1, 6, 7, 8]);
                        list_1 = __values(list), list_1_1 = list_1.next();
                        _b.label = 2;
                    case 2:
                        if (!!list_1_1.done) return [3 /*break*/, 5];
                        context = list_1_1.value;
                        return [4 /*yield*/, this.send(context, useRetry)];
                    case 3:
                        _b.sent();
                        _b.label = 4;
                    case 4:
                        list_1_1 = list_1.next();
                        return [3 /*break*/, 2];
                    case 5: return [3 /*break*/, 8];
                    case 6:
                        e_1_1 = _b.sent();
                        e_1 = { error: e_1_1 };
                        return [3 /*break*/, 8];
                    case 7:
                        try {
                            if (list_1_1 && !list_1_1.done && (_a = list_1.return)) _a.call(list_1);
                        }
                        finally { if (e_1) throw e_1.error; }
                        return [7 /*endfinally*/];
                    case 8: return [2 /*return*/];
                }
            });
        });
    };
    SessionReplayTrackDestination.prototype.send = function (context, useRetry) {
        var _a, _b;
        if (useRetry === void 0) { useRetry = true; }
        return __awaiter(this, void 0, void 0, function () {
            var apiKey, deviceId, url, version, sampleRate, urlParams, sessionReplayLibrary, payload, options, serverUrl, res, responseBody, e_2;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        apiKey = context.apiKey;
                        if (!apiKey) {
                            return [2 /*return*/, this.completeRequest({ context: context, err: MISSING_API_KEY_MESSAGE })];
                        }
                        deviceId = context.deviceId;
                        if (!deviceId) {
                            return [2 /*return*/, this.completeRequest({ context: context, err: MISSING_DEVICE_ID_MESSAGE })];
                        }
                        url = getCurrentUrl();
                        version = VERSION;
                        sampleRate = context.sampleRate;
                        urlParams = new URLSearchParams({
                            device_id: deviceId,
                            session_id: "".concat(context.sessionId),
                            type: "".concat(context.type),
                        });
                        sessionReplayLibrary = "".concat(((_a = context.version) === null || _a === void 0 ? void 0 : _a.type) || 'standalone', "/").concat(((_b = context.version) === null || _b === void 0 ? void 0 : _b.version) || version);
                        payload = this.payloadBatcher({
                            version: 1,
                            events: context.events,
                        });
                        if (payload.events.length === 0) {
                            this.completeRequest({ context: context });
                            return [2 /*return*/];
                        }
                        _c.label = 1;
                    case 1:
                        _c.trys.push([1, 6, , 7]);
                        options = {
                            headers: {
                                'Content-Type': 'application/json',
                                Accept: '*/*',
                                Authorization: "Bearer ".concat(apiKey),
                                'X-Client-Version': version,
                                'X-Client-Library': sessionReplayLibrary,
                                'X-Client-Url': url.substring(0, MAX_URL_LENGTH),
                                'X-Client-Sample-Rate': "".concat(sampleRate),
                                'X-Sampling-Hash-Alg': 'xxhash32',
                            },
                            body: JSON.stringify(payload),
                            method: 'POST',
                        };
                        serverUrl = "".concat(getServerUrl(context.serverZone, this.trackServerUrl), "?").concat(urlParams.toString());
                        return [4 /*yield*/, fetch(serverUrl, options)];
                    case 2:
                        res = _c.sent();
                        if (res === null) {
                            this.completeRequest({ context: context, err: UNEXPECTED_ERROR_MESSAGE });
                            return [2 /*return*/];
                        }
                        if (!!useRetry) return [3 /*break*/, 3];
                        responseBody = '';
                        try {
                            responseBody = JSON.stringify(res.body, null, 2);
                        }
                        catch (_d) {
                            // to avoid crash, but don't care about the error, add comment to avoid empty block lint error
                        }
                        this.completeRequest({ context: context, success: "".concat(res.status, ": ").concat(responseBody) });
                        return [3 /*break*/, 5];
                    case 3: return [4 /*yield*/, this.handleReponse(res.status, context)];
                    case 4:
                        _c.sent();
                        _c.label = 5;
                    case 5: return [3 /*break*/, 7];
                    case 6:
                        e_2 = _c.sent();
                        this.completeRequest({ context: context, err: e_2 });
                        return [3 /*break*/, 7];
                    case 7: return [2 /*return*/];
                }
            });
        });
    };
    SessionReplayTrackDestination.prototype.handleReponse = function (status, context) {
        return __awaiter(this, void 0, void 0, function () {
            var parsedStatus, _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        parsedStatus = new BaseTransport().buildStatus(status);
                        _a = parsedStatus;
                        switch (_a) {
                            case Status.Success: return [3 /*break*/, 1];
                            case Status.Failed: return [3 /*break*/, 2];
                        }
                        return [3 /*break*/, 4];
                    case 1:
                        this.handleSuccessResponse(context);
                        return [3 /*break*/, 5];
                    case 2: return [4 /*yield*/, this.handleOtherResponse(context)];
                    case 3:
                        _b.sent();
                        return [3 /*break*/, 5];
                    case 4:
                        this.completeRequest({ context: context, err: UNEXPECTED_NETWORK_ERROR_MESSAGE });
                        _b.label = 5;
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    SessionReplayTrackDestination.prototype.handleSuccessResponse = function (context) {
        var sizeOfEventsList = Math.round(new Blob(context.events).size / KB_SIZE);
        this.completeRequest({
            context: context,
            success: "Session replay event batch tracked successfully for session id ".concat(context.sessionId, ", size of events: ").concat(sizeOfEventsList, " KB"),
        });
    };
    SessionReplayTrackDestination.prototype.handleOtherResponse = function (context) {
        return __awaiter(this, void 0, void 0, function () {
            var delay;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        delay = context.attempts * this.retryTimeout;
                        context.attempts++;
                        if (context.attempts > (context.flushMaxRetries || 0)) {
                            this.completeRequest({ context: context, err: MAX_RETRIES_EXCEEDED_MESSAGE });
                            return [2 /*return*/];
                        }
                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, delay); })];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, this.send(context, true)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    SessionReplayTrackDestination.prototype.completeRequest = function (_a) {
        var context = _a.context, err = _a.err, success = _a.success;
        void context.onComplete();
        if (err) {
            this.loggerProvider.warn(err);
        }
        else if (success) {
            this.loggerProvider.log(success);
        }
    };
    return SessionReplayTrackDestination;
}());
export { SessionReplayTrackDestination };
//# sourceMappingURL=track-destination.js.map