import { Observable } from 'lib0/observable';
import { SessionType } from '@multiplayer-app/session-recorder-common';
import { TracerBrowserSDK } from './otel';
import { RecorderBrowserSDK } from './rrweb';
import { getStoredItem, setStoredItem, getNavigatorInfo, getFormattedDate, getTimeDifferenceInSeconds, isSessionActive, getOrCreateTabId, } from './utils';
import { SessionState } from './types';
import { BASE_CONFIG, SESSION_RESPONSE, SESSION_PROP_NAME, SESSION_ID_PROP_NAME, SESSION_TYPE_PROP_NAME, SESSION_STATE_PROP_NAME, DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE, getSessionRecorderConfig, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, SESSION_STARTED_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_SAVE_BUFFER_EVENT, } from './config';
import { setShouldRecordHttpData, setMaxCapturingHttpPayloadSize } from './patch';
import { recorderEventBus } from './eventBus';
import { SessionWidget } from './sessionWidget';
import messagingService from './services/messaging.service';
import { ApiService } from './services/api.service';
import { SocketService } from './services/socket.service';
import { IndexedDBService } from './services/indexedDb.service';
import { CrashBufferService } from './services/crashBuffer.service';
import { ContinuousRecordingSaveButtonState } from './sessionWidget/buttonStateConfigs';
import { NavigationRecorder } from './navigation';
export class SessionRecorder extends Observable {
    get navigation() {
        return this._navigationRecorder.api;
    }
    get isInitialized() {
        return this._isInitialized;
    }
    get sessionId() {
        return this._sessionId;
    }
    set sessionId(sessionId) {
        this._sessionId = sessionId;
        setStoredItem(SESSION_ID_PROP_NAME, sessionId);
    }
    get sessionType() {
        return this._sessionType;
    }
    set sessionType(sessionType) {
        this._sessionType = sessionType;
        const continuousRecording = sessionType === SessionType.CONTINUOUS;
        this._sessionWidget.updateContinuousRecordingState(continuousRecording);
        messagingService.sendMessage('continuous-debugging', continuousRecording);
        setStoredItem(SESSION_TYPE_PROP_NAME, sessionType);
    }
    get continuousRecording() {
        return this.sessionType === SessionType.CONTINUOUS;
    }
    get sessionState() {
        return this._sessionState || SessionState.stopped;
    }
    set sessionState(state) {
        this._sessionState = state;
        this._sessionWidget.updateState(this._sessionState, this.continuousRecording);
        messagingService.sendMessage('state-change', this._sessionState);
        setStoredItem(SESSION_STATE_PROP_NAME, state);
        // Emit observable event to support React wrapper
        this.emit('state-change', [this._sessionState || SessionState.stopped, this.sessionType]);
    }
    get session() {
        return this._session;
    }
    set session(session) {
        this._session = session;
        setStoredItem(SESSION_PROP_NAME, this._session);
    }
    get sessionAttributes() {
        return this._sessionAttributes || {};
    }
    set sessionAttributes(attributes) {
        this._sessionAttributes = attributes;
    }
    get userAttributes() {
        return this._userAttributes;
    }
    set userAttributes(userAttributes) {
        this._userAttributes = userAttributes;
    }
    get error() {
        return this._error;
    }
    set error(v) {
        this._error = v;
        this._sessionWidget.error = v;
        this.emit('error', [v]);
    }
    /**
     * Returns the HTML button element for the session widget's recorder button.
     *
     * This element is used to control the start/stop recording functionality in the session widget UI.
     *
     * @returns {HTMLButtonElement | null} The recorder button element from the session widget.
     */
    get sessionWidgetButtonElement() {
        return this._sessionWidget.recorderButton;
    }
    /**
     * Initialize debugger with default or custom configurations
     */
    constructor() {
        var _a;
        super();
        this._apiService = new ApiService();
        this._socketService = new SocketService();
        this._tracer = new TracerBrowserSDK();
        this._recorder = new RecorderBrowserSDK();
        this._sessionWidget = new SessionWidget();
        this._navigationRecorder = new NavigationRecorder();
        this._startRequestController = null;
        this._tabId = getOrCreateTabId();
        this._bufferDb = new IndexedDBService();
        this._crashBuffer = null;
        this._isFlushingBuffer = false;
        this._bufferLifecycleHandlersRegistered = false;
        this._isInitialized = false;
        // Session ID and state are stored in sessionStorage (with fallback) to avoid multi-tab conflicts
        this._sessionId = null;
        this._sessionType = SessionType.MANUAL;
        this._sessionState = null;
        this._session = null;
        this._sessionAttributes = null;
        this._userAttributes = null;
        /**
         * Error message getter and setter
         */
        this._error = '';
        // Safety: avoid accessing storage in SSR/non-browser environments
        const isBrowser = typeof window !== 'undefined';
        const sessionLocal = isBrowser ? getStoredItem(SESSION_PROP_NAME, true) : null;
        const sessionIdLocal = isBrowser ? getStoredItem(SESSION_ID_PROP_NAME) : null;
        const sessionStateLocal = isBrowser ? getStoredItem(SESSION_STATE_PROP_NAME) : null;
        const sessionTypeLocal = isBrowser ? getStoredItem(SESSION_TYPE_PROP_NAME) : null;
        if (isSessionActive(sessionLocal, sessionTypeLocal)) {
            this.session = sessionLocal;
            this.sessionId = sessionIdLocal;
            this.sessionType = sessionTypeLocal;
            this.sessionState = sessionStateLocal;
        }
        else {
            this.session = null;
            this.sessionId = null;
            this.sessionState = null;
            this.sessionType = SessionType.MANUAL;
        }
        this._configs = {
            ...BASE_CONFIG,
            apiKey: ((_a = this.session) === null || _a === void 0 ? void 0 : _a.tempApiKey) || '',
        };
    }
    /**
     * Initialize the session debugger
     * @param configs - custom configurations for session debugger
     */
    init(configs) {
        if (typeof window === 'undefined') {
            return;
        }
        this._configs = getSessionRecorderConfig({ ...this._configs, ...configs });
        this._isInitialized = true;
        this._checkOperation('init');
        // GC: remove orphaned crash buffers from old tabs.
        // Keep TTL large to avoid any accidental data loss.
        void this._bufferDb.sweepStaleTabs(10 * 60 * 60 * 1000);
        setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize || DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE);
        setShouldRecordHttpData(this._configs.captureBody, this._configs.captureHeaders);
        this._setupCrashBuffer();
        this._tracer.init(this._configs);
        this._apiService.init(this._configs);
        this._sessionWidget.init(this._configs);
        this._socketService.init({
            apiKey: this._configs.apiKey,
            clientId: this._tracer.clientId,
            socketUrl: this._configs.apiBaseUrl || '',
            keepAlive: Boolean(this._configs.useWebsocket),
            usePostMessageFallback: Boolean(this._configs.usePostMessageFallback),
        });
        this._navigationRecorder.init({
            version: this._configs.version,
            application: this._configs.application,
            environment: this._configs.environment,
            enabled: this._configs.recordNavigation,
        });
        if (this._configs.apiKey) {
            this._recorder.init(this._configs, this._socketService);
        }
        if (this.sessionId && (this.sessionState === SessionState.started || this.sessionState === SessionState.paused)) {
            this._start();
        }
        else {
            // Buffer-only recording when there is no active debug session.
            this._startBufferOnlyRecording();
        }
        this._registerWidgetEvents();
        this._registerSocketServiceListeners();
        messagingService.sendMessage('state-change', this.sessionState);
        // Emit init observable event
        this.emit('init', [this]);
    }
    _setupCrashBuffer() {
        var _a;
        if ((_a = this._configs.buffering) === null || _a === void 0 ? void 0 : _a.enabled) {
            const windowMinutes = this._configs.buffering.windowMinutes || 0.5;
            const windowMs = Math.max(10000, windowMinutes * 60 * 1000);
            this._crashBuffer = new CrashBufferService(this._bufferDb, this._tabId, windowMs);
            this._recorder.setCrashBuffer(this._crashBuffer);
            this._tracer.setCrashBuffer(this._crashBuffer);
            this._crashBuffer.on('error-span-appended', (payload) => {
                if (this.sessionState !== SessionState.stopped || this.sessionId)
                    return;
                if (!payload.span)
                    return;
                this._createExceptionSession(payload.span);
            });
            this._registerCrashBufferLifecycleHandlers();
        }
    }
    _registerCrashBufferLifecycleHandlers() {
        if (this._bufferLifecycleHandlersRegistered)
            return;
        if (typeof window === 'undefined')
            return;
        if (typeof document === 'undefined')
            return;
        if (!this._crashBuffer)
            return;
        this._bufferLifecycleHandlersRegistered = true;
        const update = () => this._updateCrashBufferActiveState();
        window.addEventListener('focus', update, { passive: true });
        window.addEventListener('blur', update, { passive: true });
        document.addEventListener('visibilitychange', update, { passive: true });
        // Set initial state.
        update();
    }
    _updateCrashBufferActiveState() {
        if (!this._crashBuffer)
            return;
        if (typeof document === 'undefined')
            return;
        let hasFocus = true;
        try {
            hasFocus = typeof document.hasFocus === 'function' ? document.hasFocus() : true;
        }
        catch (_e) {
            hasFocus = true;
        }
        const isVisible = document.visibilityState === 'visible' && !document.hidden;
        const active = isVisible && hasFocus;
        this._crashBuffer.setActive(active);
        if (active && this._crashBuffer.needsFullSnapshot()) {
            // If the buffer was cleared while inactive, the next stored rrweb event must be a FullSnapshot.
            this._recorder.takeFullSnapshot();
        }
    }
    _startBufferOnlyRecording() {
        var _a, _b;
        if (this.sessionId ||
            !this._crashBuffer ||
            !((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) ||
            this.sessionState !== SessionState.stopped) {
            return;
        }
        void this._recorder.restart(null, SessionType.MANUAL);
    }
    /**
     * Save the continuous recording session
     */
    async save() {
        try {
            this._checkOperation('save');
            if (!this.continuousRecording || !this._configs.showContinuousRecording) {
                return;
            }
            this._sessionWidget.updateSaveContinuousDebugSessionState(ContinuousRecordingSaveButtonState.SAVING);
            const res = await this._apiService.saveContinuousDebugSession(this.sessionId, {
                sessionAttributes: this.sessionAttributes,
                resourceAttributes: getNavigatorInfo(),
                stoppedAt: this._recorder.stoppedAt,
                name: this._getSessionName(),
            });
            this._sessionWidget.updateSaveContinuousDebugSessionState(ContinuousRecordingSaveButtonState.SAVED);
            const sessionUrl = res === null || res === void 0 ? void 0 : res.url;
            this._sessionWidget.showToast({
                type: 'success',
                message: 'Your session was saved',
                button: {
                    text: 'Open session',
                    url: sessionUrl,
                },
            }, 5000);
            return res;
        }
        catch (error) {
            this.error = error.message;
            this._sessionWidget.updateSaveContinuousDebugSessionState(ContinuousRecordingSaveButtonState.ERROR);
        }
        finally {
            setTimeout(() => {
                this._sessionWidget.updateSaveContinuousDebugSessionState(ContinuousRecordingSaveButtonState.IDLE);
            }, 3000);
        }
    }
    /**
     * Start a new session
     * @param type - the type of session to start
     * @param session - the session to start
     */
    start(type = SessionType.MANUAL, session) {
        this._checkOperation('start');
        // If continuous recording is disabled, force plain mode
        if (type === SessionType.CONTINUOUS && !this._configs.showContinuousRecording) {
            type = SessionType.MANUAL;
        }
        this.sessionType = type;
        this._startRequestController = new AbortController();
        if (session) {
            this._setupSessionAndStart(session, true);
        }
        else {
            this._createSessionAndStart();
        }
    }
    /**
     * Stop the current session with an optional comment
     * @param comment - user-provided comment to include in session session attributes
     */
    async stop(comment) {
        try {
            this._checkOperation('stop');
            this._stop();
            if (this.continuousRecording) {
                await this._apiService.stopContinuousDebugSession(this.sessionId);
                this.sessionType = SessionType.MANUAL;
            }
            else {
                const request = {
                    sessionAttributes: { comment },
                    stoppedAt: this._recorder.stoppedAt,
                };
                const response = await this._apiService.stopSession(this.sessionId, request);
                recorderEventBus.emit(SESSION_RESPONSE, response);
            }
            this._clearSession();
        }
        catch (error) {
            this.error = error.message;
        }
    }
    /**
     * Pause the current session
     */
    async pause() {
        try {
            this._checkOperation('pause');
            this._pause();
        }
        catch (error) {
            this.error = error.message;
        }
    }
    /**
     * Resume the current session
     */
    async resume() {
        try {
            this._checkOperation('resume');
            this._resume();
        }
        catch (error) {
            this.error = error.message;
        }
    }
    /**
     * Cancel the current session
     */
    async cancel() {
        try {
            this._checkOperation('cancel');
            this._stop();
            if (this.continuousRecording) {
                await this._apiService.stopContinuousDebugSession(this.sessionId);
                this.sessionType = SessionType.MANUAL;
            }
            else {
                await this._apiService.cancelSession(this.sessionId);
            }
            this._clearSession();
        }
        catch (error) {
            this.error = error.message;
        }
    }
    /**
     * Set the session attributes
     * @param attributes - the attributes to set
     */
    setSessionAttributes(attributes) {
        this._sessionAttributes = attributes;
    }
    /**
     * Set the user attributes
     * @param userAttributes - the user attributes to set
     */
    setUserAttributes(userAttributes) {
        if (!this._userAttributes && !userAttributes) {
            return;
        }
        this._userAttributes = userAttributes;
        const data = {
            userAttributes: this._userAttributes,
            clientId: this._tracer.clientId,
        };
        this._socketService.setUser(data);
    }
    /**
     * Updates the button click handler in the library.
     * @param handler - A function that will be invoked when the button is clicked.
     *                  The function receives the click event as its parameter and
     *                  should return `false` to prevent the default button action,
     *                  or `true` (or nothing) to allow it.
     */
    set recordingButtonClickHandler(handler) {
        this._sessionWidget.buttonClickExternalHandler = handler;
    }
    /**
     * Capture an exception manually and send it as an error trace.
     */
    captureException(error, errorInfo) {
        try {
            const normalizedError = this._normalizeError(error);
            const normalizedErrorInfo = this._normalizeErrorInfo(errorInfo);
            this._tracer.captureException(normalizedError, normalizedErrorInfo);
        }
        catch (e) {
            this.error = (e === null || e === void 0 ? void 0 : e.message) || 'Failed to capture exception';
        }
    }
    async _flushBuffer(sessionId) {
        var _a, _b;
        if (!sessionId ||
            !this._crashBuffer ||
            this._isFlushingBuffer ||
            !((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) ||
            this.sessionState !== SessionState.stopped) {
            return null;
        }
        this._isFlushingBuffer = true;
        try {
            const { events, spans, startedAt, stoppedAt } = await this._crashBuffer.snapshot();
            if (events.length === 0 && spans.length === 0) {
                return null;
            }
            await Promise.all([
                this._tracer.exportTraces(spans.map((s) => s.span)),
                this._apiService.exportEvents(sessionId, { events: events.map((e) => e.event) }),
                this._apiService.updateSessionAttributes(sessionId, {
                    startedAt: new Date(startedAt).toISOString(),
                    stoppedAt: new Date(stoppedAt).toISOString(),
                    sessionAttributes: this.sessionAttributes,
                    resourceAttributes: getNavigatorInfo(),
                    userAttributes: this._userAttributes || undefined,
                }),
            ]);
        }
        catch (_e) {
            // swallow: flush is best-effort; never throw into app code
        }
        finally {
            await this._crashBuffer.clear();
            this._isFlushingBuffer = false;
        }
    }
    /**
     * @description Check if session should be started/stopped automatically
     * @param {ISession} [sessionPayload]
     * @returns {Promise<void>}
     */
    async checkRemoteContinuousSession(sessionPayload) {
        this._checkOperation('autoStartRemoteContinuousSession');
        if (!this._configs.showContinuousRecording) {
            return;
        }
        const payload = {
            sessionAttributes: {
                ...this.sessionAttributes,
                ...((sessionPayload === null || sessionPayload === void 0 ? void 0 : sessionPayload.sessionAttributes) || {}),
            },
            resourceAttributes: {
                ...getNavigatorInfo(),
                ...((sessionPayload === null || sessionPayload === void 0 ? void 0 : sessionPayload.resourceAttributes) || {}),
            },
            userAttributes: this._userAttributes,
        };
        const { state } = await this._apiService.checkRemoteSession(payload);
        if (state == 'START') {
            if (this.sessionState !== SessionState.started) {
                await this.start(SessionType.CONTINUOUS);
            }
        }
        else if (state == 'STOP') {
            if (this.sessionState !== SessionState.stopped) {
                await this.stop();
            }
        }
    }
    /**
     * Register session widget event listeners for controlling session actions
     */
    _registerWidgetEvents() {
        this._sessionWidget.on('start', () => {
            this.error = '';
            this._handleStart();
        });
        this._sessionWidget.on('stop', (comment) => {
            this.error = '';
            this._handleStop(comment);
        });
        this._sessionWidget.on('pause', () => {
            this.error = '';
            this._handlePause();
        });
        this._sessionWidget.on('resume', () => {
            this.error = '';
            this._handleResume();
        });
        this._sessionWidget.on('cancel', () => {
            this.error = '';
            this._handleCancel();
        });
        this._sessionWidget.on('continuous-debugging', (enabled) => {
            this.error = '';
            if (enabled) {
                this._handleContinuousDebugging();
            }
            else {
                this._handleStop();
            }
        });
        this._sessionWidget.on('save', () => {
            this.error = '';
            this._handleSave();
        });
    }
    /**
     * Handle the safe start event
     */
    _handleStart() {
        if (this.sessionState === SessionState.stopped) {
            this.start(SessionType.MANUAL);
        }
    }
    /**
     * Handle the safe stop event
     */
    _handleStop(comment) {
        if (this.sessionState === SessionState.started || this.sessionState === SessionState.paused) {
            this.stop(comment);
        }
    }
    /**
     * Handle the safe pause event
     */
    _handlePause() {
        if (this.sessionState === SessionState.started) {
            this.pause();
        }
    }
    /**
     * Handle the safe resume event
     */
    _handleResume() {
        if (this.sessionState === SessionState.paused) {
            this.resume();
        }
    }
    /**
     * Handle the safe cancel event
     */
    _handleCancel() {
        if (this.sessionState === SessionState.started || this.sessionState === SessionState.paused) {
            this.cancel();
        }
    }
    /**
     * Handle the safe save event
     */
    _handleSave() {
        if (this.sessionState === SessionState.started && this.continuousRecording) {
            this.save();
        }
    }
    /**
     * Handle the safe continuous debugging event
     */
    _handleContinuousDebugging() {
        if (this.sessionState === SessionState.stopped) {
            this.start(SessionType.CONTINUOUS);
        }
    }
    /**
     * Register socket service event listeners
     */
    _registerSocketServiceListeners() {
        this._socketService.on(SESSION_STOPPED_EVENT, () => {
            this._stop();
            this._clearSession();
            this._sessionWidget.handleUIReseting();
        });
        this._socketService.on(SESSION_AUTO_CREATED, (payload) => {
            var _a;
            if (!(payload === null || payload === void 0 ? void 0 : payload.data))
                return;
            this._sessionWidget.showToast({
                type: 'success',
                message: 'Your session was auto-saved due to an error',
                button: {
                    text: 'Open session',
                    url: (_a = payload === null || payload === void 0 ? void 0 : payload.data) === null || _a === void 0 ? void 0 : _a.url,
                },
            }, 5000);
        });
        this._socketService.on(REMOTE_SESSION_RECORDING_START, (payload) => {
            if (this.sessionState === SessionState.stopped) {
                this.start();
            }
        });
        this._socketService.on(REMOTE_SESSION_RECORDING_STOP, (payload) => {
            if (this.sessionState !== SessionState.stopped) {
                this.stop();
            }
        });
        this._socketService.on(SESSION_SAVE_BUFFER_EVENT, (payload) => {
            var _a;
            this._flushBuffer((_a = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _a === void 0 ? void 0 : _a._id);
        });
    }
    async _createExceptionSession(span) {
        try {
            const session = await this._apiService.createErrorSession({ span });
            if (session === null || session === void 0 ? void 0 : session._id) {
                this._flushBuffer(session._id);
            }
            // eslint-disable-next-line no-empty
        }
        catch (_ignored) { }
    }
    /**
     * Create a new session and start it
     */
    async _createSessionAndStart() {
        var _a;
        const signal = (_a = this._startRequestController) === null || _a === void 0 ? void 0 : _a.signal;
        try {
            const payload = {
                sessionAttributes: this.sessionAttributes,
                resourceAttributes: getNavigatorInfo(),
                name: this._getSessionName(),
                ...(this._userAttributes ? { userAttributes: this._userAttributes } : {}),
            };
            const request = !this.continuousRecording ? payload : { debugSessionData: payload };
            const session = this.continuousRecording
                ? await this._apiService.startContinuousDebugSession(request, signal)
                : await this._apiService.startSession(request, signal);
            if (session) {
                session.sessionType = this.continuousRecording ? SessionType.CONTINUOUS : SessionType.MANUAL;
                this._setupSessionAndStart(session, false);
            }
        }
        catch (error) {
            this.error = error.message;
            this.sessionState = SessionState.stopped;
            if (this.continuousRecording) {
                this.sessionType = SessionType.MANUAL;
            }
        }
    }
    /**
     * Start tracing and recording for the session
     */
    _start() {
        var _a;
        this.sessionState = SessionState.started;
        // eslint-disable-next-line no-self-assign
        this.sessionType = this.sessionType;
        this._tracer.start(this.sessionId, this.sessionType);
        // Ensure we switch from buffer-only recording to session recording cleanly.
        void this._recorder.restart(this.sessionId, this.sessionType);
        this._navigationRecorder.start({ sessionId: this.sessionId, sessionType: this.sessionType });
        if (this.session) {
            recorderEventBus.emit(SESSION_STARTED_EVENT, this.session);
            this._socketService.subscribeToSession(this.session);
            this._sessionWidget.seconds = getTimeDifferenceInSeconds((_a = this.session) === null || _a === void 0 ? void 0 : _a.startedAt);
        }
    }
    /**
     * Stop tracing and recording for the session
     */
    _stop() {
        this.sessionState = SessionState.stopped;
        this._socketService.unsubscribeFromSession(true);
        this._tracer.stop();
        this._recorder.stop();
        this._navigationRecorder.stop();
        this._startBufferOnlyRecording();
    }
    /**
     * Pause the session tracing and recording
     */
    _pause() {
        this._tracer.stop();
        this._recorder.stop();
        this._navigationRecorder.pause();
        this.sessionState = SessionState.paused;
    }
    /**
     * Resume the session tracing and recording
     */
    _resume() {
        this._tracer.start(this.sessionId, this.sessionType);
        void this._recorder.restart(this.sessionId, this.sessionType);
        this._navigationRecorder.resume();
        this.sessionState = SessionState.started;
    }
    _setupSessionAndStart(session, configureExporters = true) {
        if (configureExporters && session.tempApiKey) {
            this._configs.apiKey = session.tempApiKey;
            this._socketService.updateConfigs({ apiKey: this._configs.apiKey });
            this._recorder.init(this._configs, this._socketService);
            this._tracer.setApiKey(session.tempApiKey);
            this._apiService.updateConfigs({ apiKey: this._configs.apiKey });
        }
        this._setSession(session);
        this._start();
    }
    /**
     * Set the session ID in sessionStorage (with fallback)
     * @param sessionId - the session ID to set or clear
     */
    _setSession(session) {
        this.session = { ...session, startedAt: session.startedAt || new Date().toISOString() };
        this.sessionId = (session === null || session === void 0 ? void 0 : session.shortId) || (session === null || session === void 0 ? void 0 : session._id);
    }
    _clearSession() {
        this.session = null;
        this.sessionId = null;
        this.sessionState = SessionState.stopped;
    }
    /**
     * Check the operation validity based on the session state and action
     * @param action - action being checked ('init', 'start', 'stop', 'cancel', 'pause', 'resume')
     */
    _checkOperation(action, payload) {
        if (!this._isInitialized) {
            throw new Error('Configuration not initialized. Call init() before performing any actions.');
        }
        switch (action) {
            case 'start':
                if (this.sessionState === SessionState.started) {
                    throw new Error('Session is already started.');
                }
                break;
            case 'stop':
                if (this.sessionState !== SessionState.paused && this.sessionState !== SessionState.started) {
                    throw new Error('Cannot stop. Session is not currently started.');
                }
                break;
            case 'cancel':
                if (this.sessionState === SessionState.stopped) {
                    throw new Error('Cannot cancel. Session has already been stopped.');
                }
                break;
            case 'pause':
                if (this.sessionState !== SessionState.started) {
                    throw new Error('Cannot pause. Session is not running.');
                }
                break;
            case 'resume':
                if (this.sessionState !== SessionState.paused) {
                    throw new Error('Cannot resume. Session is not paused.');
                }
                break;
            case 'save':
                if (!this.continuousRecording) {
                    throw new Error('Cannot save continuous recording session. Continuous recording is not enabled.');
                }
                if (this.sessionState !== SessionState.started) {
                    throw new Error('Cannot save continuous recording session. Session is not started.');
                }
                break;
            case 'autoStartRemoteContinuousSession':
                if (this.sessionState !== SessionState.stopped) {
                    throw new Error('Cannot start remote continuous session. Session is not stopped.');
                }
                break;
        }
    }
    _normalizeError(error) {
        if (error instanceof Error)
            return error;
        if (typeof error === 'string')
            return new Error(error);
        try {
            return new Error(JSON.stringify(error));
        }
        catch (_e) {
            return new Error(String(error));
        }
    }
    _normalizeErrorInfo(errorInfo) {
        if (!errorInfo)
            return {};
        try {
            return JSON.parse(JSON.stringify(errorInfo));
        }
        catch (_e) {
            return { errorInfo: String(errorInfo) };
        }
    }
    /**
     * Get the session name
     * @returns the session name
     */
    _getSessionName(date = new Date()) {
        var _a, _b, _c;
        const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) || ((_b = this._userAttributes) === null || _b === void 0 ? void 0 : _b.userName) || ((_c = this._userAttributes) === null || _c === void 0 ? void 0 : _c.name) || '';
        return userName
            ? `${userName}'s session on ${getFormattedDate(date, { month: 'short', day: 'numeric' })}`
            : `Session on ${getFormattedDate(date)}`;
    }
}
//# sourceMappingURL=session-recorder.js.map