import { __assign, __awaiter, __generator, __read, __spreadArray } from "tslib";
import { getAnalyticsConnector, getGlobalScope, Logger, returnWrapper, SpecialEventType, generateHashCode, isTimestampInSample, } from '@amplitude/analytics-core';
// Import only specific types to avoid pulling in the entire rrweb-types package
import { EventType as RRWebEventType } from '@amplitude/rrweb-types';
import { createSessionReplayJoinedConfigGenerator } from './config/joined-config';
import { BLOCK_CLASS, CustomRRwebEvent, DEFAULT_SESSION_REPLAY_PROPERTY, INTERACTION_MAX_INTERVAL, INTERACTION_MIN_INTERVAL, MASK_TEXT_CLASS, SESSION_REPLAY_DEBUG_PROPERTY, } from './constants';
import { EventCompressor } from './events/event-compressor';
import { createEventsManager } from './events/events-manager';
import { MultiEventManager } from './events/multi-manager';
import { getDebugConfig, getPageUrl, getStorageSize, maskFn } from './helpers';
import { clickBatcher, ClickHandler, clickNonBatcher } from './hooks/click';
import { ScrollWatcher } from './hooks/scroll';
import { SessionIdentifiers } from './identifiers';
import { SafeLoggerProvider } from './logger';
import { evaluateTargetingAndStore } from './targeting/targeting-manager';
import { VERSION } from './version';
import { createUrlTrackingPlugin } from './plugins/url-tracking-plugin';
var SessionReplay = /** @class */ (function () {
    function SessionReplay() {
        var _this = this;
        this.name = '@amplitude/session-replay-browser';
        this.recordCancelCallback = null;
        this.eventCount = 0;
        this.sessionTargetingMatch = false;
        // Visible for testing only
        this.pageLeaveFns = [];
        // Cache the dynamically imported record function
        this.recordFunction = null;
        this.teardownEventListeners = function (teardown) {
            var globalScope = getGlobalScope();
            if (globalScope) {
                globalScope.removeEventListener('blur', _this.blurListener);
                globalScope.removeEventListener('focus', _this.focusListener);
                !teardown && globalScope.addEventListener('blur', _this.blurListener);
                !teardown && globalScope.addEventListener('focus', _this.focusListener);
                // prefer pagehide to unload events, this is the standard going forward. it is not
                // 100% reliable, but is bfcache-compatible.
                if (globalScope.self && 'onpagehide' in globalScope.self) {
                    globalScope.removeEventListener('pagehide', _this.pageLeaveListener);
                    !teardown && globalScope.addEventListener('pagehide', _this.pageLeaveListener);
                }
                else {
                    // this has performance implications, but is the only way we can reliably send events
                    // in browser that don't support pagehide.
                    globalScope.removeEventListener('beforeunload', _this.pageLeaveListener);
                    !teardown && globalScope.addEventListener('beforeunload', _this.pageLeaveListener);
                }
            }
        };
        this.blurListener = function () {
            _this.sendEvents();
        };
        this.focusListener = function () {
            // Restart recording on focus to ensure that when user
            // switches tabs, we take a full snapshot
            void _this.recordEvents(false);
        };
        /**
         * This is an instance member so that if init is called multiple times
         * it doesn't add another listener to the page leave event. This is to
         * prevent duplicate listener actions from firing.
         */
        this.pageLeaveListener = function (e) {
            _this.pageLeaveFns.forEach(function (fn) {
                fn(e);
            });
        };
        this.evaluateTargetingAndCapture = function (targetingParams, isInit, forceRestart) {
            if (isInit === void 0) { isInit = false; }
            if (forceRestart === void 0) { forceRestart = false; }
            return __awaiter(_this, void 0, void 0, function () {
                var eventForTargeting, _a;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0:
                            if (!this.identifiers || !this.identifiers.sessionId || !this.config) {
                                if (this.identifiers && !this.identifiers.sessionId) {
                                    this.loggerProvider.log('Session ID has not been set yet, cannot evaluate targeting for Session Replay.');
                                }
                                else {
                                    this.loggerProvider.warn('Session replay init has not been called, cannot evaluate targeting.');
                                }
                                return [2 /*return*/];
                            }
                            // Handle cases where there's no targeting config
                            if (!this.config.targetingConfig) {
                                if (isInit) {
                                    this.loggerProvider.log('Targeting config has not been set yet, cannot evaluate targeting.');
                                }
                                else {
                                    this.loggerProvider.log('No targeting config set, skipping initialization/recording for event.');
                                    return [2 /*return*/];
                                }
                            }
                            // Store targeting parameters for use in getShouldRecord
                            this.lastTargetingParams = targetingParams;
                            if (!(this.config.targetingConfig && !this.sessionTargetingMatch)) return [3 /*break*/, 2];
                            eventForTargeting = targetingParams.event;
                            if (eventForTargeting &&
                                Object.values(SpecialEventType).includes(eventForTargeting.event_type)) {
                                eventForTargeting = undefined;
                            }
                            // We're setting this on this class because fetching the value from idb
                            // is async, we need to access this value synchronously (for record
                            // and for getSessionReplayProperties - both synchronous fns)
                            _a = this;
                            return [4 /*yield*/, evaluateTargetingAndStore({
                                    sessionId: this.identifiers.sessionId,
                                    targetingConfig: this.config.targetingConfig,
                                    loggerProvider: this.loggerProvider,
                                    apiKey: this.config.apiKey,
                                    targetingParams: { userProperties: targetingParams.userProperties, event: eventForTargeting },
                                })];
                        case 1:
                            // We're setting this on this class because fetching the value from idb
                            // is async, we need to access this value synchronously (for record
                            // and for getSessionReplayProperties - both synchronous fns)
                            _a.sessionTargetingMatch = _b.sent();
                            // Log the targeting config to debug
                            this.loggerProvider.debug(JSON.stringify({
                                name: 'targeted replay capture config',
                                sessionTargetingMatch: this.sessionTargetingMatch,
                                event: eventForTargeting,
                                targetingParams: targetingParams,
                            }, null, 2));
                            _b.label = 2;
                        case 2:
                            if (!isInit) return [3 /*break*/, 3];
                            void this.initialize(true);
                            return [3 /*break*/, 5];
                        case 3:
                            if (!(forceRestart || !this.recordCancelCallback)) return [3 /*break*/, 5];
                            // Only call recordEvents if we're not already recording, unless forceRestart is true
                            this.loggerProvider.log('Recording events for session due to forceRestart or no ongoing recording.');
                            return [4 /*yield*/, this.recordEvents()];
                        case 4:
                            _b.sent();
                            _b.label = 5;
                        case 5: return [2 /*return*/];
                    }
                });
            });
        };
        this.addCustomRRWebEvent = function (eventName, eventData, addStorageInfo) {
            if (eventData === void 0) { eventData = {}; }
            if (addStorageInfo === void 0) { addStorageInfo = true; }
            return __awaiter(_this, void 0, void 0, function () {
                var debugInfo, config, storageSizeData, e_1;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            _a.trys.push([0, 3, , 4]);
                            debugInfo = undefined;
                            config = this.config;
                            if (!(config && eventName !== CustomRRwebEvent.METADATA)) return [3 /*break*/, 2];
                            debugInfo = {
                                config: getDebugConfig(config),
                                version: VERSION,
                            };
                            if (!addStorageInfo) return [3 /*break*/, 2];
                            return [4 /*yield*/, getStorageSize()];
                        case 1:
                            storageSizeData = _a.sent();
                            debugInfo = __assign(__assign({}, storageSizeData), debugInfo);
                            _a.label = 2;
                        case 2:
                            // Check first to ensure we are recording
                            if (this.recordCancelCallback && this.recordFunction) {
                                this.recordFunction.addCustomEvent(eventName, __assign(__assign({}, eventData), debugInfo));
                            }
                            else {
                                this.loggerProvider.debug("Not able to add custom replay capture event ".concat(eventName, " due to no ongoing recording."));
                            }
                            return [3 /*break*/, 4];
                        case 3:
                            e_1 = _a.sent();
                            this.loggerProvider.debug('Error while adding custom replay capture event: ', e_1);
                            return [3 /*break*/, 4];
                        case 4: return [2 /*return*/];
                    }
                });
            });
        };
        this.stopRecordingEvents = function () {
            var _a;
            try {
                _this.loggerProvider.log('Session Replay capture stopping.');
                _this.recordCancelCallback && _this.recordCancelCallback();
                _this.recordCancelCallback = null;
                (_a = _this.networkObservers) === null || _a === void 0 ? void 0 : _a.stop();
            }
            catch (error) {
                var typedError = error;
                _this.loggerProvider.warn("Error occurred while stopping replay capture: ".concat(typedError.toString()));
            }
        };
        this.loggerProvider = new SafeLoggerProvider(new Logger());
    }
    SessionReplay.prototype.init = function (apiKey, options) {
        return returnWrapper(this._init(apiKey, options));
    };
    SessionReplay.prototype._init = function (apiKey, options) {
        var _a, _b, _c, _d, _e, _f, _g;
        return __awaiter(this, void 0, void 0, function () {
            var _h, _j, joinedConfig, localConfig, remoteConfig, scrollWatcher, managers, storeType, rrwebEventManager, error_1, typedError, payloadBatcher, interactionEventManager, error_2, typedError, workerScript, globalScope, compressionScript;
            return __generator(this, function (_k) {
                switch (_k.label) {
                    case 0:
                        this.loggerProvider = new SafeLoggerProvider(options.loggerProvider || new Logger());
                        Object.prototype.hasOwnProperty.call(options, 'logLevel') &&
                            this.loggerProvider.enable(options.logLevel);
                        this.identifiers = new SessionIdentifiers({ sessionId: options.sessionId, deviceId: options.deviceId });
                        _h = this;
                        return [4 /*yield*/, createSessionReplayJoinedConfigGenerator(apiKey, options)];
                    case 1:
                        _h.joinedConfigGenerator = _k.sent();
                        return [4 /*yield*/, this.joinedConfigGenerator.generateJoinedConfig()];
                    case 2:
                        _j = _k.sent(), joinedConfig = _j.joinedConfig, localConfig = _j.localConfig, remoteConfig = _j.remoteConfig;
                        this.config = joinedConfig;
                        this.setMetadata(options.sessionId, joinedConfig, localConfig, remoteConfig, (_a = options.version) === null || _a === void 0 ? void 0 : _a.version, VERSION, (_b = options.version) === null || _b === void 0 ? void 0 : _b.type);
                        if (options.sessionId && ((_c = this.config.interactionConfig) === null || _c === void 0 ? void 0 : _c.enabled)) {
                            scrollWatcher = ScrollWatcher.default({
                                sessionId: options.sessionId,
                                type: 'interaction',
                            }, this.config);
                            this.pageLeaveFns = [scrollWatcher.send(this.getDeviceId.bind(this)).bind(scrollWatcher)];
                            this.scrollHook = scrollWatcher.hook.bind(scrollWatcher);
                            this.clickHandler = new ClickHandler(this.loggerProvider, scrollWatcher);
                        }
                        managers = [];
                        storeType = this.config.storeType;
                        if (storeType === 'idb' && !((_d = getGlobalScope()) === null || _d === void 0 ? void 0 : _d.indexedDB)) {
                            storeType = 'memory';
                            this.loggerProvider.warn('Could not use preferred indexedDB storage, reverting to in memory option.');
                        }
                        this.loggerProvider.log("Using ".concat(storeType, " for event storage."));
                        _k.label = 3;
                    case 3:
                        _k.trys.push([3, 5, , 6]);
                        return [4 /*yield*/, createEventsManager({
                                config: this.config,
                                sessionId: this.identifiers.sessionId,
                                type: 'replay',
                                storeType: storeType,
                            })];
                    case 4:
                        rrwebEventManager = _k.sent();
                        managers.push({ name: 'replay', manager: rrwebEventManager });
                        return [3 /*break*/, 6];
                    case 5:
                        error_1 = _k.sent();
                        typedError = error_1;
                        this.loggerProvider.warn("Error occurred while creating replay events manager: ".concat(typedError.toString()));
                        return [3 /*break*/, 6];
                    case 6:
                        if (!((_e = this.config.interactionConfig) === null || _e === void 0 ? void 0 : _e.enabled)) return [3 /*break*/, 10];
                        payloadBatcher = this.config.interactionConfig.batch ? clickBatcher : clickNonBatcher;
                        _k.label = 7;
                    case 7:
                        _k.trys.push([7, 9, , 10]);
                        return [4 /*yield*/, createEventsManager({
                                config: this.config,
                                sessionId: this.identifiers.sessionId,
                                type: 'interaction',
                                minInterval: (_f = this.config.interactionConfig.trackEveryNms) !== null && _f !== void 0 ? _f : INTERACTION_MIN_INTERVAL,
                                maxInterval: INTERACTION_MAX_INTERVAL,
                                payloadBatcher: payloadBatcher,
                                storeType: storeType,
                            })];
                    case 8:
                        interactionEventManager = _k.sent();
                        managers.push({ name: 'interaction', manager: interactionEventManager });
                        return [3 /*break*/, 10];
                    case 9:
                        error_2 = _k.sent();
                        typedError = error_2;
                        this.loggerProvider.warn("Error occurred while creating interaction events manager: ".concat(typedError.toString()));
                        return [3 /*break*/, 10];
                    case 10:
                        this.eventsManager = new (MultiEventManager.bind.apply(MultiEventManager, __spreadArray([void 0], __read(managers), false)))();
                        // To prevent too many threads.
                        if (this.eventCompressor) {
                            this.eventCompressor.terminate();
                        }
                        workerScript = undefined;
                        globalScope = getGlobalScope();
                        if (!(((_g = this.config.experimental) === null || _g === void 0 ? void 0 : _g.useWebWorker) && globalScope && globalScope.Worker)) return [3 /*break*/, 12];
                        return [4 /*yield*/, import('./worker')];
                    case 11:
                        compressionScript = (_k.sent()).compressionScript;
                        workerScript = compressionScript;
                        _k.label = 12;
                    case 12:
                        this.eventCompressor = new EventCompressor(this.eventsManager, this.config, this.getDeviceId(), workerScript);
                        return [4 /*yield*/, this.initializeNetworkObservers()];
                    case 13:
                        _k.sent();
                        this.loggerProvider.log('Installing @amplitude/session-replay-browser.');
                        this.teardownEventListeners(false);
                        return [4 /*yield*/, this.evaluateTargetingAndCapture({ userProperties: options.userProperties }, true)];
                    case 14:
                        _k.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    SessionReplay.prototype.setSessionId = function (sessionId, deviceId) {
        return returnWrapper(this.asyncSetSessionId(sessionId, deviceId));
    };
    SessionReplay.prototype.asyncSetSessionId = function (sessionId, deviceId, options) {
        var _a;
        return __awaiter(this, void 0, void 0, function () {
            var previousSessionId, deviceIdForReplayId, joinedConfig;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        this.sessionTargetingMatch = false;
                        this.lastShouldRecordDecision = undefined; // Reset targeting decision for new session
                        previousSessionId = this.identifiers && this.identifiers.sessionId;
                        if (previousSessionId) {
                            this.sendEvents(previousSessionId);
                        }
                        deviceIdForReplayId = deviceId || this.getDeviceId();
                        this.identifiers = new SessionIdentifiers({
                            sessionId: sessionId,
                            deviceId: deviceIdForReplayId,
                        });
                        if (!(this.joinedConfigGenerator && previousSessionId)) return [3 /*break*/, 2];
                        return [4 /*yield*/, this.joinedConfigGenerator.generateJoinedConfig()];
                    case 1:
                        joinedConfig = (_b.sent()).joinedConfig;
                        this.config = joinedConfig;
                        _b.label = 2;
                    case 2:
                        if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.targetingConfig)) return [3 /*break*/, 4];
                        return [4 /*yield*/, this.evaluateTargetingAndCapture({ userProperties: options === null || options === void 0 ? void 0 : options.userProperties }, false, true)];
                    case 3:
                        _b.sent();
                        return [3 /*break*/, 6];
                    case 4: return [4 /*yield*/, this.recordEvents()];
                    case 5:
                        _b.sent();
                        _b.label = 6;
                    case 6: return [2 /*return*/];
                }
            });
        });
    };
    SessionReplay.prototype.getSessionReplayProperties = function () {
        var _a;
        var config = this.config;
        var identifiers = this.identifiers;
        if (!config || !identifiers) {
            this.loggerProvider.warn('Session replay init has not been called, cannot get session replay properties.');
            return {};
        }
        var shouldRecord = this.getShouldRecord();
        var eventProperties = {};
        if (shouldRecord) {
            eventProperties = (_a = {},
                _a[DEFAULT_SESSION_REPLAY_PROPERTY] = identifiers.sessionReplayId ? identifiers.sessionReplayId : null,
                _a);
            if (config.debugMode) {
                eventProperties[SESSION_REPLAY_DEBUG_PROPERTY] = JSON.stringify({
                    appHash: generateHashCode(config.apiKey).toString(),
                });
            }
        }
        void this.addCustomRRWebEvent(CustomRRwebEvent.GET_SR_PROPS, {
            shouldRecord: shouldRecord,
            eventProperties: eventProperties,
        }, this.eventCount === 10);
        if (this.eventCount === 10) {
            this.eventCount = 0;
        }
        this.eventCount++;
        return eventProperties;
    };
    SessionReplay.prototype.sendEvents = function (sessionId) {
        var _a;
        var sessionIdToSend = sessionId || ((_a = this.identifiers) === null || _a === void 0 ? void 0 : _a.sessionId);
        var deviceId = this.getDeviceId();
        this.eventsManager &&
            sessionIdToSend &&
            deviceId &&
            this.eventsManager.sendCurrentSequenceEvents({ sessionId: sessionIdToSend, deviceId: deviceId });
    };
    SessionReplay.prototype.initialize = function (shouldSendStoredEvents) {
        var _a;
        if (shouldSendStoredEvents === void 0) { shouldSendStoredEvents = false; }
        return __awaiter(this, void 0, void 0, function () {
            var deviceId;
            return __generator(this, function (_b) {
                if (!((_a = this.identifiers) === null || _a === void 0 ? void 0 : _a.sessionId)) {
                    this.loggerProvider.log("Session is not being recorded due to lack of session id.");
                    return [2 /*return*/, Promise.resolve()];
                }
                deviceId = this.getDeviceId();
                if (!deviceId) {
                    this.loggerProvider.log("Session is not being recorded due to lack of device id.");
                    return [2 /*return*/, Promise.resolve()];
                }
                this.eventsManager && shouldSendStoredEvents && void this.eventsManager.sendStoredEvents({ deviceId: deviceId });
                return [2 /*return*/, this.recordEvents()];
            });
        });
    };
    SessionReplay.prototype.shouldOptOut = function () {
        var _a, _b;
        var identityStoreOptOut;
        if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.instanceName) {
            var identityStore = getAnalyticsConnector(this.config.instanceName).identityStore;
            identityStoreOptOut = identityStore.getIdentity().optOut;
        }
        return identityStoreOptOut !== undefined ? identityStoreOptOut : (_b = this.config) === null || _b === void 0 ? void 0 : _b.optOut;
    };
    SessionReplay.prototype.getShouldRecord = function () {
        if (!this.identifiers || !this.config || !this.identifiers.sessionId) {
            this.loggerProvider.warn("Session is not being recorded due to lack of config, please call sessionReplay.init.");
            return false;
        }
        if (!this.config.captureEnabled) {
            this.loggerProvider.log("Session ".concat(this.identifiers.sessionId, " not being captured due to capture being disabled for project or because the remote config could not be fetched."));
            return false;
        }
        if (this.shouldOptOut()) {
            this.loggerProvider.log("Opting session ".concat(this.identifiers.sessionId, " out of recording due to optOut config."));
            return false;
        }
        var shouldRecord = false;
        var message = '';
        var matched = false;
        // If targetingConfig exists, we'll use the sessionTargetingMatch to determine whether to record
        // Otherwise, we'll evaluate the session against the overall sample rate
        if (this.config.targetingConfig) {
            if (!this.sessionTargetingMatch) {
                message = "Not capturing replays for session ".concat(this.identifiers.sessionId, " due to not matching targeting conditions.");
                this.loggerProvider.log(message);
                shouldRecord = false;
                matched = false;
            }
            else {
                message = "Capturing replays for session ".concat(this.identifiers.sessionId, " due to matching targeting conditions.");
                this.loggerProvider.log(message);
                shouldRecord = true;
                matched = true;
            }
        }
        else {
            var isInSample = isTimestampInSample(this.identifiers.sessionId, this.config.sampleRate);
            if (!isInSample) {
                message = "Opting session ".concat(this.identifiers.sessionId, " out of recording due to sample rate.");
                this.loggerProvider.log(message);
                shouldRecord = false;
                matched = false;
            }
            else {
                shouldRecord = true;
                matched = true;
            }
        }
        // Only send custom rrweb event for targeting decision when the decision changes
        if (this.lastShouldRecordDecision !== shouldRecord && this.config.targetingConfig) {
            void this.addCustomRRWebEvent(CustomRRwebEvent.TARGETING_DECISION, {
                message: message,
                sessionId: this.identifiers.sessionId,
                matched: matched,
                targetingParams: this.lastTargetingParams,
            });
            this.lastShouldRecordDecision = shouldRecord;
        }
        return shouldRecord;
    };
    SessionReplay.prototype.getBlockSelectors = function () {
        var _a, _b, _c;
        // For some reason, this defaults to empty array ([]) if undefined in the compiled script.
        // Empty arrays cause errors when being evaluated in Safari.
        // Force the selector to be undefined if it's an empty array.
        var blockSelector = (_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.privacyConfig) === null || _b === void 0 ? void 0 : _b.blockSelector) !== null && _c !== void 0 ? _c : [];
        if (blockSelector.length === 0) {
            return undefined;
        }
        return blockSelector;
    };
    SessionReplay.prototype.getMaskTextSelectors = function () {
        var _a, _b, _c, _d;
        if (((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.privacyConfig) === null || _b === void 0 ? void 0 : _b.defaultMaskLevel) === 'conservative') {
            return '*';
        }
        var maskSelector = (_d = (_c = this.config) === null || _c === void 0 ? void 0 : _c.privacyConfig) === null || _d === void 0 ? void 0 : _d.maskSelector;
        if (!maskSelector) {
            return;
        }
        return maskSelector;
    };
    SessionReplay.prototype.getRecordingPlugins = function (loggingConfig) {
        var _a, _b, _c, _d, _e, _f;
        return __awaiter(this, void 0, void 0, function () {
            var plugins, urlTrackingPlugin, getRecordConsolePlugin, error_3;
            return __generator(this, function (_g) {
                switch (_g.label) {
                    case 0:
                        plugins = [];
                        // Add URL tracking plugin
                        try {
                            urlTrackingPlugin = createUrlTrackingPlugin({
                                ugcFilterRules: ((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.interactionConfig) === null || _b === void 0 ? void 0 : _b.ugcFilterRules) || [],
                                enablePolling: ((_c = this.config) === null || _c === void 0 ? void 0 : _c.enableUrlChangePolling) || false,
                                pollingInterval: (_d = this.config) === null || _d === void 0 ? void 0 : _d.urlChangePollingInterval,
                                captureDocumentTitle: (_e = this.config) === null || _e === void 0 ? void 0 : _e.captureDocumentTitle,
                            });
                            plugins.push(urlTrackingPlugin);
                        }
                        catch (error) {
                            this.loggerProvider.warn('Failed to create URL tracking plugin:', error);
                        }
                        if (!((_f = loggingConfig === null || loggingConfig === void 0 ? void 0 : loggingConfig.console) === null || _f === void 0 ? void 0 : _f.enabled)) return [3 /*break*/, 4];
                        _g.label = 1;
                    case 1:
                        _g.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, import('@amplitude/rrweb-plugin-console-record')];
                    case 2:
                        getRecordConsolePlugin = (_g.sent()).getRecordConsolePlugin;
                        plugins.push(getRecordConsolePlugin({ level: loggingConfig.console.levels }));
                        return [3 /*break*/, 4];
                    case 3:
                        error_3 = _g.sent();
                        this.loggerProvider.warn('Failed to load console plugin:', error_3);
                        return [3 /*break*/, 4];
                    case 4: return [2 /*return*/, plugins.length > 0 ? plugins : undefined];
                }
            });
        });
    };
    SessionReplay.prototype.getRecordFunction = function () {
        return __awaiter(this, void 0, void 0, function () {
            var record, error_4;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (this.recordFunction) {
                            return [2 /*return*/, this.recordFunction];
                        }
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, import('@amplitude/rrweb-record')];
                    case 2:
                        record = (_a.sent()).record;
                        this.recordFunction = record;
                        return [2 /*return*/, record];
                    case 3:
                        error_4 = _a.sent();
                        this.loggerProvider.warn('Failed to load rrweb-record module:', error_4);
                        return [2 /*return*/, null];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    SessionReplay.prototype.recordEvents = function (shouldLogMetadata) {
        var _a, _b, _c, _d, _e, _f;
        if (shouldLogMetadata === void 0) { shouldLogMetadata = true; }
        return __awaiter(this, void 0, void 0, function () {
            var config, shouldRecord, sessionId, recordFunction, privacyConfig, interactionConfig, loggingConfig, hooks, ugcFilterRules, _g, _h, error_5;
            var _j;
            var _this = this;
            return __generator(this, function (_k) {
                switch (_k.label) {
                    case 0:
                        config = this.config;
                        shouldRecord = this.getShouldRecord();
                        sessionId = (_a = this.identifiers) === null || _a === void 0 ? void 0 : _a.sessionId;
                        if (!shouldRecord || !sessionId || !config) {
                            return [2 /*return*/];
                        }
                        this.stopRecordingEvents();
                        return [4 /*yield*/, this.getRecordFunction()];
                    case 1:
                        recordFunction = _k.sent();
                        // May be undefined if cannot import rrweb-record
                        if (!recordFunction) {
                            return [2 /*return*/];
                        }
                        return [4 /*yield*/, this.initializeNetworkObservers()];
                    case 2:
                        _k.sent();
                        (_b = this.networkObservers) === null || _b === void 0 ? void 0 : _b.start(function (event) {
                            void _this.addCustomRRWebEvent(CustomRRwebEvent.FETCH_REQUEST, event);
                        });
                        privacyConfig = config.privacyConfig, interactionConfig = config.interactionConfig, loggingConfig = config.loggingConfig;
                        hooks = (interactionConfig === null || interactionConfig === void 0 ? void 0 : interactionConfig.enabled)
                            ? {
                                mouseInteraction: this.eventsManager &&
                                    ((_c = this.clickHandler) === null || _c === void 0 ? void 0 : _c.createHook({
                                        eventsManager: this.eventsManager,
                                        sessionId: sessionId,
                                        deviceIdFn: this.getDeviceId.bind(this),
                                        mirror: recordFunction.mirror,
                                        ugcFilterRules: (_d = interactionConfig.ugcFilterRules) !== null && _d !== void 0 ? _d : [],
                                    })),
                                scroll: this.scrollHook,
                            }
                            : {};
                        ugcFilterRules = (interactionConfig === null || interactionConfig === void 0 ? void 0 : interactionConfig.enabled) && interactionConfig.ugcFilterRules ? interactionConfig.ugcFilterRules : [];
                        this.loggerProvider.log("Session Replay capture beginning for ".concat(sessionId, "."));
                        _k.label = 3;
                    case 3:
                        _k.trys.push([3, 5, , 6]);
                        _g = this;
                        _h = recordFunction;
                        _j = {
                            emit: function (event) {
                                if (_this.shouldOptOut()) {
                                    _this.loggerProvider.log("Opting session ".concat(sessionId, " out of recording due to optOut config."));
                                    _this.stopRecordingEvents();
                                    _this.sendEvents();
                                    return;
                                }
                                if (event.type === RRWebEventType.Meta) {
                                    event.data.href = getPageUrl(event.data.href, ugcFilterRules);
                                }
                                if (_this.eventCompressor) {
                                    // Schedule processing during idle time if the browser supports requestIdleCallback
                                    _this.eventCompressor.enqueueEvent(event, sessionId);
                                }
                            },
                            inlineStylesheet: config.shouldInlineStylesheet,
                            hooks: hooks,
                            maskAllInputs: true,
                            maskTextClass: MASK_TEXT_CLASS,
                            blockClass: BLOCK_CLASS,
                            blockSelector: this.getBlockSelectors(),
                            applyBackgroundColorToBlockedElements: config.applyBackgroundColorToBlockedElements,
                            maskInputFn: maskFn('input', privacyConfig),
                            maskTextFn: maskFn('text', privacyConfig),
                            maskTextSelector: this.getMaskTextSelectors(),
                            recordCanvas: false,
                            slimDOMOptions: {
                                script: (_e = config.omitElementTags) === null || _e === void 0 ? void 0 : _e.script,
                                comment: (_f = config.omitElementTags) === null || _f === void 0 ? void 0 : _f.comment,
                            },
                            errorHandler: function (error) {
                                var typedError = error;
                                // styled-components relies on this error being thrown and bubbled up, rrweb is otherwise suppressing it
                                if (typedError.message.includes('insertRule') && typedError.message.includes('CSSStyleSheet')) {
                                    throw typedError;
                                }
                                // rrweb does monkey patching on certain window functions such as CSSStyleSheet.proptype.insertRule,
                                // and errors from external clients calling these functions can get suppressed. Styled components depend
                                // on these errors being re-thrown.
                                if (typedError._external_) {
                                    throw typedError;
                                }
                                _this.loggerProvider.warn('Error while capturing replay: ', typedError.toString());
                                // Return true so that we don't clutter user's consoles with internal rrweb errors
                                return true;
                            }
                        };
                        return [4 /*yield*/, this.getRecordingPlugins(loggingConfig)];
                    case 4:
                        _g.recordCancelCallback = _h.apply(void 0, [(_j.plugins = _k.sent(),
                                _j)]);
                        void this.addCustomRRWebEvent(CustomRRwebEvent.DEBUG_INFO);
                        if (shouldLogMetadata) {
                            void this.addCustomRRWebEvent(CustomRRwebEvent.METADATA, this.metadata);
                        }
                        return [3 /*break*/, 6];
                    case 5:
                        error_5 = _k.sent();
                        this.loggerProvider.warn('Failed to initialize session replay:', error_5);
                        return [3 /*break*/, 6];
                    case 6: return [2 /*return*/];
                }
            });
        });
    };
    SessionReplay.prototype.getDeviceId = function () {
        var _a;
        return (_a = this.identifiers) === null || _a === void 0 ? void 0 : _a.deviceId;
    };
    SessionReplay.prototype.getSessionId = function () {
        var _a;
        return (_a = this.identifiers) === null || _a === void 0 ? void 0 : _a.sessionId;
    };
    SessionReplay.prototype.flush = function (useRetry) {
        var _a;
        if (useRetry === void 0) { useRetry = false; }
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_b) {
                return [2 /*return*/, (_a = this.eventsManager) === null || _a === void 0 ? void 0 : _a.flush(useRetry)];
            });
        });
    };
    SessionReplay.prototype.shutdown = function () {
        this.teardownEventListeners(true);
        this.stopRecordingEvents();
        this.sendEvents();
    };
    SessionReplay.prototype.mapSDKType = function (sdkType) {
        if (sdkType === 'plugin') {
            return '@amplitude/plugin-session-replay-browser';
        }
        if (sdkType === 'segment') {
            return '@amplitude/segment-session-replay-plugin';
        }
        return null;
    };
    SessionReplay.prototype.setMetadata = function (sessionId, joinedConfig, localConfig, remoteConfig, replaySDKVersion, standaloneSDKVersion, sdkType) {
        var hashValue = (sessionId === null || sessionId === void 0 ? void 0 : sessionId.toString()) ? generateHashCode(sessionId.toString()) : undefined;
        this.metadata = {
            joinedConfig: joinedConfig,
            localConfig: localConfig,
            remoteConfig: remoteConfig,
            sessionId: sessionId,
            hashValue: hashValue,
            sampleRate: joinedConfig.sampleRate,
            replaySDKType: this.mapSDKType(sdkType),
            replaySDKVersion: replaySDKVersion,
            standaloneSDKType: '@amplitude/session-replay-browser',
            standaloneSDKVersion: standaloneSDKVersion,
        };
    };
    SessionReplay.prototype.initializeNetworkObservers = function () {
        var _a, _b, _c;
        return __awaiter(this, void 0, void 0, function () {
            var NetworkObserversClass, error_6;
            return __generator(this, function (_d) {
                switch (_d.label) {
                    case 0:
                        if (!(((_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.loggingConfig) === null || _b === void 0 ? void 0 : _b.network) === null || _c === void 0 ? void 0 : _c.enabled) && !this.networkObservers)) return [3 /*break*/, 4];
                        _d.label = 1;
                    case 1:
                        _d.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, import('./observers')];
                    case 2:
                        NetworkObserversClass = (_d.sent()).NetworkObservers;
                        this.networkObservers = new NetworkObserversClass();
                        return [3 /*break*/, 4];
                    case 3:
                        error_6 = _d.sent();
                        this.loggerProvider.warn('Failed to import or instantiate NetworkObservers:', error_6);
                        return [3 /*break*/, 4];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    return SessionReplay;
}());
export { SessionReplay };
//# sourceMappingURL=session-replay.js.map