import io from 'socket.io-client';
import { Observable } from 'lib0/observable';
import messagingService from '../services/messaging.service';
import { SESSION_ADD_EVENT, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, SESSION_SUBSCRIBE_EVENT, SESSION_UNSUBSCRIBE_EVENT, SOCKET_SET_USER_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_STARTED_EVENT, } from '../config';
const MAX_RECONNECTION_ATTEMPTS = 2;
export class SocketService extends Observable {
    constructor() {
        super();
        this.socket = null;
        this.queue = [];
        this.isConnecting = false;
        this.isConnected = false;
        this.attempts = 0;
        this.sessionId = null;
        this.usePostMessage = false;
        this.options = {
            apiKey: '',
            socketUrl: '',
            keepAlive: false,
            usePostMessageFallback: false,
        };
    }
    /**
     * Initialize the socket service
     * @param config - Socket service configuration
     */
    init(config) {
        this.options = {
            ...this.options,
            ...config,
        };
        if (this.options.keepAlive &&
            this.options.socketUrl &&
            this.options.apiKey) {
            this._initConnection();
        }
    }
    /**
     * Update the socket service configuration
     * @param config - Partial configuration to update
     */
    updateConfigs(config) {
        var _a;
        // If any config changed, reconnect if connected
        const hasChanges = Object.keys(config).some((key) => {
            const typedKey = key;
            return (config[typedKey] !== undefined &&
                config[typedKey] !== this.options[typedKey]);
        });
        if (hasChanges) {
            this.options = { ...this.options, ...config };
            if ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected) {
                this.close().then(() => {
                    if (this.options.keepAlive &&
                        this.options.socketUrl &&
                        this.options.apiKey) {
                        this._initConnection();
                    }
                });
            }
        }
    }
    _initConnection() {
        if (this.isConnecting || this.isConnected)
            return;
        this.attempts++;
        this.isConnecting = true;
        this.usePostMessage = false;
        this.socket = io(this.options.socketUrl, {
            path: '/v0/radar/ws',
            auth: {
                'x-api-key': this.options.apiKey,
            },
            reconnectionAttempts: 2,
            transports: ['websocket'],
        });
        this.socket.on('ready', () => {
            this.isConnecting = false;
            this.isConnected = true;
            this.usePostMessage = false;
            this.flushQueue();
        });
        this.socket.on('disconnect', (err) => {
            this.isConnecting = false;
            this.isConnected = false;
        });
        this.socket.on('connect_error', (err) => {
            this.isConnecting = false;
            this.isConnected = false;
            this.checkReconnectionAttempts();
        });
        this.socket.on(SESSION_STOPPED_EVENT, (data) => {
            this.emit(SESSION_STOPPED_EVENT, [data]);
        });
        this.socket.on(SESSION_AUTO_CREATED, (data) => {
            this.emit(SESSION_AUTO_CREATED, [data]);
        });
        this.socket.on(REMOTE_SESSION_RECORDING_START, (data) => {
            this.emit(REMOTE_SESSION_RECORDING_START, [data]);
        });
        this.socket.on(REMOTE_SESSION_RECORDING_STOP, (data) => {
            this.emit(REMOTE_SESSION_RECORDING_STOP, [data]);
        });
    }
    checkReconnectionAttempts() {
        if (this.attempts >= MAX_RECONNECTION_ATTEMPTS) {
            this.usePostMessage = !!this.options.usePostMessageFallback;
            this.flushQueue();
        }
    }
    sendViaPostMessage(name, data) {
        const action = name === SESSION_ADD_EVENT ? 'rrweb-event' : 'socket-emit';
        messagingService.sendMessage(action, data);
    }
    emitSocketEvent(name, data) {
        var _a;
        if (this.usePostMessage) {
            this.sendViaPostMessage(name, data);
        }
        else if ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected) {
            this.socket.emit(name, data);
        }
        else {
            this.queue.push({ data, name });
            this._initConnection();
        }
    }
    flushQueue() {
        var _a, _b;
        while (this.queue.length > 0 && (this.usePostMessage || ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected))) {
            const event = this.queue.shift();
            if (!event)
                continue;
            if (this.usePostMessage) {
                this.sendViaPostMessage(event.name, event.data);
            }
            else if ((_b = this.socket) === null || _b === void 0 ? void 0 : _b.connected) {
                this.socket.emit(event.name, event.data);
            }
        }
    }
    send(event) {
        this.emitSocketEvent(SESSION_ADD_EVENT, event);
    }
    subscribeToSession(session) {
        this.sessionId = session.shortId || session._id;
        const payload = {
            projectId: session.project,
            workspaceId: session.workspace,
            debugSessionId: this.sessionId,
            sessionType: session.creationType,
        };
        this.emitSocketEvent(SESSION_SUBSCRIBE_EVENT, payload);
        this.emitSocketEvent(SESSION_STARTED_EVENT, { debugSessionId: this.sessionId });
    }
    unsubscribeFromSession(stopSession) {
        if (this.sessionId) {
            this.emitSocketEvent(SESSION_UNSUBSCRIBE_EVENT, { debugSessionId: this.sessionId });
            if (stopSession) {
                this.emitSocketEvent(SESSION_STOPPED_EVENT, {});
            }
        }
    }
    setUser(userAttributes) {
        this.emitSocketEvent(SOCKET_SET_USER_EVENT, userAttributes);
    }
    close() {
        return new Promise((resolve) => {
            var _a;
            if (this.usePostMessage) {
                this.sendViaPostMessage('close', {});
            }
            if ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected) {
                setTimeout(() => {
                    var _a;
                    this.unsubscribeFromSession();
                    this.attempts = 0;
                    this.isConnected = false;
                    this.isConnecting = false;
                    (_a = this.socket) === null || _a === void 0 ? void 0 : _a.disconnect();
                    this.socket = null;
                    resolve();
                }, 500);
            }
            else {
                resolve();
            }
        });
    }
}
//# sourceMappingURL=socket.service.js.map