import { __assign, __awaiter, __generator, __read, __spreadArray } from "tslib";
import { getStorageSize } from '../helpers';
import { SessionReplayTrackDestination } from '../track-destination';
import { SessionReplayEventsIDBStore } from './events-idb-store';
import { InMemoryEventsStore } from './events-memory-store';
export var createEventsManager = function (_a) {
    var config = _a.config, minInterval = _a.minInterval, maxInterval = _a.maxInterval, type = _a.type, payloadBatcher = _a.payloadBatcher, storeType = _a.storeType, trackDestinationWorkerScript = _a.trackDestinationWorkerScript;
    return __awaiter(void 0, void 0, void 0, function () {
        function flush(useRetry) {
            if (useRetry === void 0) { useRetry = false; }
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    return [2 /*return*/, trackDestination.flush(useRetry)];
                });
            });
        }
        var trackDestination, getMemoryStore, lastKnownDeviceId, usingIdbStore, store, switchToMemoryStore, getIdbStoreOrFallback, _b, beaconBuffer, beaconWindowStart, advanceBeaconWindow, sendEventsList, sendCurrentSequenceEvents, sendStoredEvents, addEvent, getBeaconEvents;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    trackDestination = new SessionReplayTrackDestination(__assign(__assign({}, config), { loggerProvider: config.loggerProvider, payloadBatcher: payloadBatcher, workerScript: trackDestinationWorkerScript }));
                    getMemoryStore = function () {
                        return new InMemoryEventsStore({
                            loggerProvider: config.loggerProvider,
                            maxInterval: maxInterval,
                            minInterval: minInterval,
                        });
                    };
                    usingIdbStore = false;
                    switchToMemoryStore = function () { return __awaiter(void 0, void 0, void 0, function () {
                        var sequences, _a, deviceId_1;
                        return __generator(this, function (_b) {
                            switch (_b.label) {
                                case 0:
                                    if (!usingIdbStore)
                                        return [2 /*return*/];
                                    usingIdbStore = false;
                                    config.loggerProvider.warn('IDB store is experiencing repeated failures; falling back to in-memory event store.');
                                    if (!lastKnownDeviceId) return [3 /*break*/, 2];
                                    return [4 /*yield*/, store.getSequencesToSend()];
                                case 1:
                                    _a = _b.sent();
                                    return [3 /*break*/, 3];
                                case 2:
                                    _a = undefined;
                                    _b.label = 3;
                                case 3:
                                    sequences = _a;
                                    store = getMemoryStore();
                                    if (sequences && lastKnownDeviceId) {
                                        deviceId_1 = lastKnownDeviceId;
                                        sequences.forEach(function (seq) {
                                            sendEventsList({ sequenceId: seq.sequenceId, events: seq.events, sessionId: seq.sessionId, deviceId: deviceId_1 });
                                        });
                                    }
                                    return [2 /*return*/];
                            }
                        });
                    }); };
                    getIdbStoreOrFallback = function () { return __awaiter(void 0, void 0, void 0, function () {
                        var idb;
                        return __generator(this, function (_a) {
                            switch (_a.label) {
                                case 0: return [4 /*yield*/, SessionReplayEventsIDBStore.new(type, {
                                        loggerProvider: config.loggerProvider,
                                        minInterval: minInterval,
                                        maxInterval: maxInterval,
                                        apiKey: config.apiKey,
                                        onPersistentFailure: function () {
                                            void switchToMemoryStore();
                                        },
                                    })];
                                case 1:
                                    idb = _a.sent();
                                    if (!idb) {
                                        config.loggerProvider.log('Failed to initialize idb store, falling back to memory store.');
                                        return [2 /*return*/, getMemoryStore()];
                                    }
                                    usingIdbStore = true;
                                    return [2 /*return*/, idb];
                            }
                        });
                    }); };
                    if (!(storeType === 'idb')) return [3 /*break*/, 2];
                    return [4 /*yield*/, getIdbStoreOrFallback()];
                case 1:
                    _b = _c.sent();
                    return [3 /*break*/, 3];
                case 2:
                    _b = getMemoryStore();
                    _c.label = 3;
                case 3:
                    store = _b;
                    beaconBuffer = [];
                    beaconWindowStart = 0;
                    advanceBeaconWindow = function (upToAbsoluteIdx) {
                        if (upToAbsoluteIdx <= beaconWindowStart)
                            return;
                        var trimCount = Math.min(upToAbsoluteIdx - beaconWindowStart, beaconBuffer.length);
                        if (trimCount > 0) {
                            beaconBuffer.splice(0, trimCount);
                            beaconWindowStart = upToAbsoluteIdx;
                        }
                    };
                    sendEventsList = function (_a) {
                        var events = _a.events, sessionId = _a.sessionId, deviceId = _a.deviceId, sequenceId = _a.sequenceId;
                        if (config.debugMode) {
                            getStorageSize()
                                .then(function (_a) {
                                var totalStorageSize = _a.totalStorageSize, percentOfQuota = _a.percentOfQuota, usageDetails = _a.usageDetails;
                                config.loggerProvider.debug("Total storage size: ".concat(totalStorageSize, " KB, percentage of quota: ").concat(percentOfQuota, "%, usage details: ").concat(usageDetails));
                            })
                                .catch(function () {
                                // swallow error
                            });
                        }
                        trackDestination.sendEventsList({
                            events: events,
                            sessionId: sessionId,
                            flushMaxRetries: config.flushMaxRetries,
                            apiKey: config.apiKey,
                            deviceId: deviceId,
                            sampleRate: config.sampleRate,
                            serverZone: config.serverZone,
                            version: config.version,
                            type: type,
                            onComplete: function () { return __awaiter(void 0, void 0, void 0, function () {
                                return __generator(this, function (_a) {
                                    switch (_a.label) {
                                        case 0: return [4 /*yield*/, store.cleanUpSessionEventsStore(sessionId, sequenceId)];
                                        case 1:
                                            _a.sent();
                                            return [2 /*return*/];
                                    }
                                });
                            }); },
                        });
                    };
                    sendCurrentSequenceEvents = function (_a) {
                        var sessionId = _a.sessionId, deviceId = _a.deviceId;
                        lastKnownDeviceId = deviceId;
                        // Snapshot the absolute end-index before the async store read so that any events
                        // pushed after this point are NOT considered sent and remain in the beacon buffer.
                        var snapshotAbsIdx = beaconWindowStart + beaconBuffer.length;
                        store
                            .storeCurrentSequence(sessionId)
                            .then(function (currentSequence) {
                            if (currentSequence) {
                                advanceBeaconWindow(snapshotAbsIdx);
                                sendEventsList({
                                    sequenceId: currentSequence.sequenceId,
                                    events: currentSequence.events,
                                    sessionId: currentSequence.sessionId,
                                    deviceId: deviceId,
                                });
                            }
                        })
                            .catch(function (e) {
                            config.loggerProvider.warn('Failed to get current sequence of session replay events for session:', e);
                        });
                    };
                    sendStoredEvents = function (_a) {
                        var deviceId = _a.deviceId;
                        return __awaiter(void 0, void 0, void 0, function () {
                            var sequencesToSend;
                            return __generator(this, function (_b) {
                                switch (_b.label) {
                                    case 0:
                                        lastKnownDeviceId = deviceId;
                                        return [4 /*yield*/, store.getSequencesToSend()];
                                    case 1:
                                        sequencesToSend = _b.sent();
                                        sequencesToSend &&
                                            sequencesToSend.forEach(function (sequence) {
                                                sendEventsList({
                                                    sequenceId: sequence.sequenceId,
                                                    events: sequence.events,
                                                    sessionId: sequence.sessionId,
                                                    deviceId: deviceId,
                                                });
                                            });
                                        return [2 /*return*/];
                                }
                            });
                        });
                    };
                    addEvent = function (_a) {
                        var event = _a.event, sessionId = _a.sessionId, deviceId = _a.deviceId;
                        lastKnownDeviceId = deviceId;
                        // Record the absolute index of this event in the beacon buffer before the async
                        // store operation. If a batch split occurs, we advance the window up to (but not
                        // including) this event so that it starts the next pending window.
                        var absIdx = beaconWindowStart + beaconBuffer.length;
                        beaconBuffer.push(event.data);
                        store
                            .addEventToCurrentSequence(sessionId, event.data)
                            .then(function (sequenceToSend) {
                            if (sequenceToSend) {
                                // Events before absIdx belong to the split batch being sent; advance window.
                                advanceBeaconWindow(absIdx);
                                sendEventsList({
                                    sequenceId: sequenceToSend.sequenceId,
                                    events: sequenceToSend.events,
                                    sessionId: sequenceToSend.sessionId,
                                    deviceId: deviceId,
                                });
                            }
                        })
                            .catch(function (e) {
                            config.loggerProvider.warn('Failed to add event to session replay capture:', e);
                        });
                    };
                    getBeaconEvents = function () { return __spreadArray([], __read(beaconBuffer), false); };
                    return [2 /*return*/, {
                            sendCurrentSequenceEvents: sendCurrentSequenceEvents,
                            addEvent: addEvent,
                            sendStoredEvents: sendStoredEvents,
                            flush: flush,
                            getBeaconEvents: getBeaconEvents,
                            trackDestination: trackDestination,
                        }];
            }
        });
    });
};
//# sourceMappingURL=events-manager.js.map