class BoruAISocket {
    constructor({
        chatUserId,
        chatVersion = '1000',
        host = null,
        debug = false
    }) {
        this.chatUserId = chatUserId;
        this.chatVersion = chatVersion;
        this.socketHost = host || (typeof WS_SOCKET_HOST !== 'undefined' ? WS_SOCKET_HOST : '');
        this.debug = debug;

        this.socket = null;
        this.connected = false;
        this.wsRCInterval = null;
        this.handlers = {};
        this.ajaxPromiseMap = {};
        this.wsIdxBase = Math.random().toString(36).split(".")[1] + "-";
    }

    setHost(host) {
        this.socketHost = host;
        //set this.socketPort if needed, default to 443 if https:// or 80 if http://, override if specified (:port)
        if (this.socketHost.startsWith('https://')) {
            this.socketPort = 443;
        } else if (this.socketHost.startsWith('http://')) {
            this.socketPort = 80;
        } else {
            this.socketPort = 80; // No port specified
        }
        //if the port is specified in the host, extract it
        const portMatch = this.socketHost.match(/:(\d+)$/);
        if (portMatch) {
            this.socketPort = parseInt(portMatch[1], 10);
        }
    }

    setDebug(debug) {
        this.debug = !!debug;
    }

    init() {
        const host = this.socketHost.includes('http://localhost')
            ? 'localhost' + (this.socketPort ? `:${this.socketPort}` : '')
            : this.socketHost;

        this.socket = io.connect(host, {
            transports: ['websocket'],
            reconnection: false
        });

        this.socket.once('connect', () => {
            this.connected = true;

            this.socket.on('disconnect', () => {
                this.connected = false;
                this.log('server disconnected. Retry timer started');
                this.wsRCInterval = setInterval(() => {
                    if (this.connected) {
                        this.log('Connection fully restored.');
                        clearInterval(this.wsRCInterval);
                        this.wsRCInterval = null;
                    } else {
                        this.reconnect();
                    }
                }, 3000);
            });

            this.emit('chat_version', parseInt(this.chatVersion));
            this.emit('register', this.chatUserId);

            for (const event in this.handlers) {
                this.socket.on(event, this.handlers[event]);
            }
        });
    }

    on(event, callback, once = false) {
        const handler = once
            ? (...args) => {
                  callback(...args);
                  this.socket.off(event, handler);
              }
            : callback;

        this.handlers[event] = handler;
        if (this.socket && this.connected) {
            this.socket.on(event, handler);
        }
    }

    once(event, callback) {
        this.on(event, callback, true);
    }

    emit(...args) {
        if (this.connected) {
            this.socket.emit(...args);
        } else {
            this.waitForConnection().then(() => this.socket.emit(...args));
        }
    }

    waitForConnection(timeout = 30000, interval = 500) {
        return new Promise((resolve, reject) => {
            const start = Date.now();
            const check = () => {
                if (this.connected) return resolve(true);
                if (Date.now() - start > timeout) return reject('timeout');
                setTimeout(check, interval);
            };
            check();
        });
    }

    reconnect() {
        this.init();
    }

    log(...args) {
        if (this.debug) { console.log('[WS]', ...args); }
    }

    info(...args) {
        if (this.debug) { console.info('[WS]', ...args); }
    }

    warn(...args) {
        if (this.debug) { console.warn('[WS]', ...args); }
    }

    error(...args) {
        if (this.debug) { console.error('[WS]', ...args); }
    }
}
