import logger from '../util/logger';
import { SwEvent } from '../util/constants';
import { safeParseJson, checkWebSocketHost, destructResponse } from '../util/helpers';
import { registerOnce, trigger } from '../services/Handler';
import { isFunction } from '../util/helpers';
let WebSocketClass = typeof WebSocket !== 'undefined' ? WebSocket : null;
export const setWebSocket = (websocket) => {
    WebSocketClass = websocket;
};
const WS_STATE = {
    CONNECTING: 0,
    OPEN: 1,
    CLOSING: 2,
    CLOSED: 3
};
const TIMEOUT_MS = 10 * 1000;
export default class Connection {
    constructor(session) {
        this.session = session;
        this._wsClient = null;
        this._host = 'wss://dev.xswitch.com:9090';
        this._timers = {};
        this.upDur = null;
        this.downDur = null;
        const { host, security } = session.options;
        if (host) {
            this._host = checkWebSocketHost(host, security);
        }
        this.connect();
    }
    get connected() {
        return this._wsClient && this._wsClient.readyState === WS_STATE.OPEN;
    }
    get connecting() {
        return this._wsClient && this._wsClient.readyState === WS_STATE.CONNECTING;
    }
    get closing() {
        return this._wsClient && this._wsClient.readyState === WS_STATE.CLOSING;
    }
    get closed() {
        return this._wsClient && this._wsClient.readyState === WS_STATE.CLOSED;
    }
    get isAlive() {
        return this.connecting || this.connected;
    }
    get isDead() {
        return this.closing || this.closed;
    }
    wsHandler(ws) {
        if (!ws)
            return;
        ws.onopen = (event) => trigger(SwEvent.SocketOpen, event, this.session.uuid);
        ws.onclose = (event) => trigger(SwEvent.SocketClose, event, this.session.uuid);
        ws.onerror = (event) => trigger(SwEvent.SocketError, event, this.session.uuid);
        ws.onmessage = (event) => {
            const msg = safeParseJson(event.data);
            if (typeof msg === 'string') {
                this._handleStringResponse(msg);
                return;
            }
            this._unsetTimer(msg.id);
            logger.debug('RECV: \n', JSON.stringify(msg, null, 2), '\n');
            if (!trigger(msg.id, msg)) {
                trigger(SwEvent.SocketMessage, msg, this.session.uuid);
            }
        };
    }
    connect() {
        if ('WebSocket' in window && WebSocketClass) {
            this._wsClient = new WebSocketClass(this._host);
            this.wsHandler(this._wsClient);
        }
    }
    sendRawText(request) {
        this._wsClient.send(request);
    }
    send(bladeObj) {
        var _a;
        const { request } = bladeObj;
        const promise = new Promise((resolve, reject) => {
            if (request.hasOwnProperty('result')) {
                return resolve(request);
            }
            registerOnce(request.id, (response) => {
                const { result, error } = destructResponse(response);
                return error ? reject(error) : resolve(result);
            });
            this._setTimer(request.id);
        });
        if (request.method !== 'Uas.Ping') {
            logger.debug('SEND: \n', JSON.stringify(request, null, 2), '\n');
        }
        (_a = this._wsClient) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(request));
        return promise;
    }
    close() {
        var _a;
        if (this._wsClient) {
            isFunction(this._wsClient._beginClose) ? this._wsClient._beginClose() : (_a = this._wsClient) === null || _a === void 0 ? void 0 : _a.close();
        }
        this._wsClient = null;
    }
    _unsetTimer(id) {
        clearTimeout(this._timers[id]);
        delete this._timers[id];
    }
    _setTimer(id) {
        this._timers[id] = setTimeout(() => {
            trigger(id, { error: { code: this.session.timeoutErrorCode, message: 'Timeout' } });
            this._unsetTimer(id);
        }, TIMEOUT_MS);
    }
    _handleStringResponse(response) {
        if (/^#SP/.test(response)) {
            switch (response[3]) {
                case 'U':
                    this.upDur = parseInt(response.substring(4));
                    break;
                case 'D':
                    this.downDur = parseInt(response.substring(4));
                    trigger(SwEvent.SpeedTest, { upDur: this.upDur, downDur: this.downDur }, this.session.uuid);
                    break;
            }
        }
        else {
            logger.warn('Unknown message from socket', response);
        }
    }
}
