var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import BaseSession from './BaseSession';
import { Connect } from './base/Uas';
import { SESSION_ID } from './util/constants';
import { rpcCallHook } from './hook';
import { isDefined } from './util/helpers';
import { Unsubscribe, Subscribe, Broadcast } from './base/Uas';
import { Request } from './base/BaseMessage';
import logger from './util/logger';
export const XCALL_PROTOCOL = 'xcall-protocol';
export default class Xcall extends BaseSession {
    constructor() {
        super(...arguments);
        this.relayProtocol = XCALL_PROTOCOL;
        this.timeoutErrorCode = -329990;
    }
    validateOptions() {
        const { host } = this.options;
        return Boolean(host);
    }
    createWebSockerAdapter(name) {
        const session = this;
        return function (method, requestData, callback) {
            let _method = `${name}.${method.name}`;
            const request = new Request(_method, requestData);
            session.execute(request).catch((err) => {
                callback(null, {
                    code: 500,
                    message: `Bad Request ${JSON.stringify(err)}`,
                });
            }).finally(() => {
            }).then((response) => {
                if (isDefined(response)) {
                    callback(null, response);
                }
                callback(null, {
                    code: 500,
                    message: `Bad Request formatting error response: "${JSON.stringify(response)}"`,
                });
            });
        };
    }
    createHttpAdapter(URI) {
        const contentType = this.options.contentType;
        const FETCH_OPTIONS = {
            method: 'POST',
            mode: 'cors',
            headers: {
                'Content-Type': contentType,
            },
        };
        return function (method, requestData, callback) {
            var options = Object.assign(Object.assign({}, FETCH_OPTIONS), { body: JSON.stringify(requestData) });
            const token = sessionStorage.getItem('XCALL_TOKEN');
            if (token) {
                options.headers = {
                    'Content-Type': contentType,
                    'Authorization': `Xcc-Token ${token}`,
                };
            }
            const url = `${URI}/${method.name}`;
            logger.debug('SEND: \n', url, '\n', JSON.stringify(requestData, null, 2), '\n');
            fetch(url, options).then((response) => {
                const ct = response.headers.get('content-type');
                if (ct == 'text/plain; charset=utf-8') {
                    response.json().then((pt) => {
                        callback(null, pt);
                    }).catch((reason) => {
                        callback(null, {
                            code: 500,
                            message: `Bad Request: HTTP status code 400; "${reason}\"`,
                        });
                    });
                }
                else if (ct == 'application/protobuf') {
                    response.arrayBuffer().then((buffer) => {
                        const arr = new Uint8Array(buffer);
                        callback(null, arr);
                    }).catch((reason) => {
                        callback(null, {
                            code: 500,
                            message: `Bad Request: HTTP status code 400; "${reason}\"`,
                        });
                    });
                }
                else if (ct == 'application/json') {
                    response.json().then((buffer) => {
                        logger.debug('RECV: \n', JSON.stringify(buffer, null, 2), '\n');
                        callback(null, buffer);
                    }).catch((reason) => {
                        callback(null, {
                            code: 500,
                            message: `Bad Request: HTTP status code 400; "${reason}\"`,
                        });
                    });
                }
                else {
                    callback(null, {
                        code: 500,
                        message: `Bad Request: HTTP status code 400; transport: received the unexpected content-type \"${ct}\"`,
                    });
                }
                return;
            }).catch((reason) => {
                callback(null, {
                    code: 500,
                    message: `Bad Request: HTTP status code 400; "${reason}\"`,
                });
            });
        };
    }
    create(service, name, stream) {
        if (!stream) {
            let scheme = "http";
            if (this.options.security) {
                scheme = "https";
            }
            let _name = name.replace(/\./g, "/");
            var s = new service(this.createHttpAdapter(`${scheme}://${this.options.host}/api/${_name}`));
        }
        else {
            var s = new service(this.createWebSockerAdapter(name));
        }
        s.rpcCall = rpcCallHook;
        return s;
    }
    broadcast({ channel: eventChannel = '', data }) {
        if (!eventChannel) {
            throw new Error('Invalid channel for broadcast: ' + eventChannel);
        }
        const msg = new Broadcast({ sessionid: this.sessionid, eventChannel, data });
        this.execute(msg).catch((error) => error);
    }
    subscribe({ channels: eventChannel = [], handler }) {
        return __awaiter(this, void 0, void 0, function* () {
            const msg = new Subscribe({ sessionid: this.sessionid, topics: eventChannel });
            const response = yield this.execute(msg);
            if (response) {
                const { code, message } = response;
                if (code == 200) {
                    eventChannel.forEach(channel => this._addSubscription(this.relayProtocol, handler, channel));
                }
                else {
                    logger.debug(`error subscribe [${code}] ${message}`);
                }
            }
            return response;
        });
    }
    unsubscribe({ channels: eventChannel = [], handler }) {
        return __awaiter(this, void 0, void 0, function* () {
            const msg = new Unsubscribe({ sessionid: this.sessionid, topics: eventChannel });
            const response = yield this.execute(msg);
            if (response) {
                const { code, message } = response;
                if (code == 200) {
                    eventChannel.forEach(channel => this._removeSubscription(this.relayProtocol, channel));
                }
                else {
                    logger.debug(`error unsubscribe [${code}] ${message}`);
                }
            }
            return response;
        });
    }
    setToken(token) {
        return __awaiter(this, void 0, void 0, function* () {
            sessionStorage.setItem('XCALL_TOKEN', token);
            const response = yield this.execute(new Connect(token, this.sessionid)).catch((error) => {
                this._handleLoginError(error);
            });
            if (response) {
                const { code } = response;
                if (code == 200) {
                    this._autoReconnect = true;
                    this.sessionid = response.sessionid;
                    sessionStorage.setItem(SESSION_ID, this.sessionid);
                }
            }
            return response;
        });
    }
    isAuthorized() {
        return this._jwtAuth;
    }
}
