var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _CoreManager = _interopRequireDefault(require("./CoreManager"));
var _ParseObject = _interopRequireDefault(require("./ParseObject"));
var _LiveQuerySubscription = _interopRequireDefault(require("./LiveQuerySubscription"));
var _promiseUtils = require("./promiseUtils");
var _ParseError = _interopRequireDefault(require("./ParseError"));
var CLIENT_STATE = {
  INITIALIZED: 'initialized',
  CONNECTING: 'connecting',
  CONNECTED: 'connected',
  CLOSED: 'closed',
  RECONNECTING: 'reconnecting',
  DISCONNECTED: 'disconnected'
};
var OP_TYPES = {
  CONNECT: 'connect',
  SUBSCRIBE: 'subscribe',
  UNSUBSCRIBE: 'unsubscribe',
  ERROR: 'error'
};
var OP_EVENTS = {
  CONNECTED: 'connected',
  SUBSCRIBED: 'subscribed',
  UNSUBSCRIBED: 'unsubscribed',
  ERROR: 'error',
  CREATE: 'create',
  UPDATE: 'update',
  ENTER: 'enter',
  LEAVE: 'leave',
  DELETE: 'delete'
};
var CLIENT_EMMITER_TYPES = {
  CLOSE: 'close',
  ERROR: 'error',
  OPEN: 'open'
};
var SUBSCRIPTION_EMMITER_TYPES = {
  OPEN: 'open',
  CLOSE: 'close',
  ERROR: 'error',
  CREATE: 'create',
  UPDATE: 'update',
  ENTER: 'enter',
  LEAVE: 'leave',
  DELETE: 'delete'
};
var generateInterval = function (k) {
  return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
};
var LiveQueryClient = function () {
  function LiveQueryClient(_ref) {
    var _this = this;
    var applicationId = _ref.applicationId,
      serverURL = _ref.serverURL,
      javascriptKey = _ref.javascriptKey,
      masterKey = _ref.masterKey,
      sessionToken = _ref.sessionToken,
      installationId = _ref.installationId;
    (0, _classCallCheck2.default)(this, LiveQueryClient);
    if (!serverURL || serverURL.indexOf('ws') !== 0) {
      throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient');
    }
    this.reconnectHandle = null;
    this.attempts = 1;
    this.id = 0;
    this.requestId = 1;
    this.serverURL = serverURL;
    this.applicationId = applicationId;
    this.javascriptKey = javascriptKey || undefined;
    this.masterKey = masterKey || undefined;
    this.sessionToken = sessionToken || undefined;
    this.installationId = installationId || undefined;
    this.additionalProperties = true;
    this.connectPromise = (0, _promiseUtils.resolvingPromise)();
    this.subscriptions = new Map();
    this.state = CLIENT_STATE.INITIALIZED;
    var EventEmitter = _CoreManager.default.getEventEmitter();
    this.emitter = new EventEmitter();
    this.on = function (eventName, listener) {
      return _this.emitter.on(eventName, listener);
    };
    this.emit = function (eventName) {
      var _this$emitter;
      for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
      }
      return (_this$emitter = _this.emitter).emit.apply(_this$emitter, [eventName].concat(args));
    };
    this.on('error', function () {});
  }
  return (0, _createClass2.default)(LiveQueryClient, [{
    key: "shouldOpen",
    value: function () {
      return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED;
    }
  }, {
    key: "subscribe",
    value: function (query, sessionToken) {
      var _queryJSON$keys,
        _queryJSON$watch,
        _this2 = this;
      if (!query) {
        return;
      }
      var className = query.className;
      var queryJSON = query.toJSON();
      var where = queryJSON.where;
      var keys = (_queryJSON$keys = queryJSON.keys) == null ? void 0 : _queryJSON$keys.split(',');
      var watch = (_queryJSON$watch = queryJSON.watch) == null ? void 0 : _queryJSON$watch.split(',');
      var subscribeRequest = {
        op: OP_TYPES.SUBSCRIBE,
        requestId: this.requestId,
        query: {
          className: className,
          where: where,
          keys: keys,
          watch: watch
        },
        sessionToken: undefined
      };
      if (sessionToken) {
        subscribeRequest.sessionToken = sessionToken;
      }
      var subscription = new _LiveQuerySubscription.default(this.requestId, query, sessionToken);
      this.subscriptions.set(this.requestId, subscription);
      this.requestId += 1;
      this.connectPromise.then(function () {
        _this2.socket.send(JSON.stringify(subscribeRequest));
      }).catch(function (error) {
        subscription.subscribePromise.reject(error);
      });
      return subscription;
    }
  }, {
    key: "unsubscribe",
    value: function () {
      var _unsubscribe = (0, _asyncToGenerator2.default)(function* (subscription) {
        var _this3 = this;
        if (!subscription) {
          return;
        }
        var unsubscribeRequest = {
          op: OP_TYPES.UNSUBSCRIBE,
          requestId: subscription.id
        };
        return this.connectPromise.then(function () {
          return _this3.socket.send(JSON.stringify(unsubscribeRequest));
        }).then(function () {
          return subscription.unsubscribePromise;
        });
      });
      function unsubscribe() {
        return _unsubscribe.apply(this, arguments);
      }
      return unsubscribe;
    }()
  }, {
    key: "open",
    value: function () {
      var _this4 = this;
      var WebSocketImplementation = _CoreManager.default.getWebSocketController();
      if (!WebSocketImplementation) {
        this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation');
        return;
      }
      if (this.state !== CLIENT_STATE.RECONNECTING) {
        this.state = CLIENT_STATE.CONNECTING;
      }
      this.socket = new WebSocketImplementation(this.serverURL);
      this.socket.closingPromise = (0, _promiseUtils.resolvingPromise)();
      this.socket.onopen = function () {
        _this4._handleWebSocketOpen();
      };
      this.socket.onmessage = function (event) {
        _this4._handleWebSocketMessage(event);
      };
      this.socket.onclose = function (event) {
        var _this4$socket$closing;
        (_this4$socket$closing = _this4.socket.closingPromise) == null ? void 0 : _this4$socket$closing.resolve(event);
        _this4._handleWebSocketClose();
      };
      this.socket.onerror = function (error) {
        _this4._handleWebSocketError(error);
      };
    }
  }, {
    key: "resubscribe",
    value: function () {
      var _this5 = this;
      this.subscriptions.forEach(function (subscription, requestId) {
        var _queryJSON$keys2, _queryJSON$watch2;
        var query = subscription.query;
        var queryJSON = query.toJSON();
        var where = queryJSON.where;
        var keys = (_queryJSON$keys2 = queryJSON.keys) == null ? void 0 : _queryJSON$keys2.split(',');
        var watch = (_queryJSON$watch2 = queryJSON.watch) == null ? void 0 : _queryJSON$watch2.split(',');
        var className = query.className;
        var sessionToken = subscription.sessionToken;
        var subscribeRequest = {
          op: OP_TYPES.SUBSCRIBE,
          requestId: requestId,
          query: {
            className: className,
            where: where,
            keys: keys,
            watch: watch
          },
          sessionToken: undefined
        };
        if (sessionToken) {
          subscribeRequest.sessionToken = sessionToken;
        }
        _this5.connectPromise.then(function () {
          _this5.socket.send(JSON.stringify(subscribeRequest));
        });
      });
    }
  }, {
    key: "close",
    value: function () {
      var _close = (0, _asyncToGenerator2.default)(function* () {
        var _this$socket, _this$socket2;
        if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) {
          return;
        }
        this.state = CLIENT_STATE.DISCONNECTED;
        (_this$socket = this.socket) == null ? void 0 : _this$socket.close();
        for (var subscription of this.subscriptions.values()) {
          subscription.subscribed = false;
          subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE);
        }
        this._handleReset();
        this.emit(CLIENT_EMMITER_TYPES.CLOSE);
        return (_this$socket2 = this.socket) == null ? void 0 : _this$socket2.closingPromise;
      });
      function close() {
        return _close.apply(this, arguments);
      }
      return close;
    }()
  }, {
    key: "_handleReset",
    value: function () {
      this.attempts = 1;
      this.id = 0;
      this.requestId = 1;
      this.connectPromise = (0, _promiseUtils.resolvingPromise)();
      this.subscriptions = new Map();
    }
  }, {
    key: "_handleWebSocketOpen",
    value: function () {
      var connectRequest = {
        op: OP_TYPES.CONNECT,
        applicationId: this.applicationId,
        javascriptKey: this.javascriptKey,
        masterKey: this.masterKey,
        sessionToken: this.sessionToken,
        installationId: undefined
      };
      if (this.additionalProperties) {
        connectRequest.installationId = this.installationId;
      }
      this.socket.send(JSON.stringify(connectRequest));
    }
  }, {
    key: "_handleWebSocketMessage",
    value: function (event) {
      var data = event.data;
      if (typeof data === 'string') {
        data = JSON.parse(data);
      }
      var subscription = null;
      if (data.requestId) {
        subscription = this.subscriptions.get(data.requestId) || null;
      }
      var response = {
        clientId: data.clientId,
        installationId: data.installationId
      };
      switch (data.op) {
        case OP_EVENTS.CONNECTED:
          if (this.state === CLIENT_STATE.RECONNECTING) {
            this.resubscribe();
          }
          this.emit(CLIENT_EMMITER_TYPES.OPEN);
          this.id = data.clientId;
          this.connectPromise.resolve();
          this.state = CLIENT_STATE.CONNECTED;
          break;
        case OP_EVENTS.SUBSCRIBED:
          if (subscription) {
            this.attempts = 1;
            subscription.subscribed = true;
            subscription.subscribePromise.resolve();
            setTimeout(function () {
              return subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN, response);
            }, 200);
          }
          break;
        case OP_EVENTS.ERROR:
          {
            var parseError = new _ParseError.default(data.code, data.error);
            if (!this.id) {
              this.connectPromise.reject(parseError);
              this.state = CLIENT_STATE.DISCONNECTED;
            }
            if (data.requestId) {
              if (subscription) {
                subscription.subscribePromise.reject(parseError);
                setTimeout(function () {
                  return subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error);
                }, 200);
              }
            } else {
              this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error);
            }
            if (data.error === 'Additional properties not allowed') {
              this.additionalProperties = false;
            }
            if (data.reconnect) {
              this._handleReconnect();
            }
            break;
          }
        case OP_EVENTS.UNSUBSCRIBED:
          {
            if (subscription) {
              this.subscriptions.delete(data.requestId);
              subscription.subscribed = false;
              subscription.unsubscribePromise.resolve();
            }
            break;
          }
        default:
          {
            if (!subscription) {
              break;
            }
            var override = false;
            if (data.original) {
              override = true;
              delete data.original.__type;
              for (var field in data.original) {
                if (!(field in data.object)) {
                  data.object[field] = undefined;
                }
              }
              data.original = _ParseObject.default.fromJSON(data.original, false);
            }
            delete data.object.__type;
            var parseObject = _ParseObject.default.fromJSON(data.object, !(subscription.query && subscription.query._select) ? override : false);
            if (data.original) {
              subscription.emit(data.op, parseObject, data.original, response);
            } else {
              subscription.emit(data.op, parseObject, response);
            }
            var localDatastore = _CoreManager.default.getLocalDatastore();
            if (override && localDatastore.isEnabled) {
              localDatastore._updateObjectIfPinned(parseObject).then(function () {});
            }
          }
      }
    }
  }, {
    key: "_handleWebSocketClose",
    value: function () {
      if (this.state === CLIENT_STATE.DISCONNECTED) {
        return;
      }
      this.state = CLIENT_STATE.CLOSED;
      this.emit(CLIENT_EMMITER_TYPES.CLOSE);
      for (var subscription of this.subscriptions.values()) {
        subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE);
      }
      this._handleReconnect();
    }
  }, {
    key: "_handleWebSocketError",
    value: function (error) {
      this.emit(CLIENT_EMMITER_TYPES.ERROR, error);
      for (var subscription of this.subscriptions.values()) {
        subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, error);
      }
      this._handleReconnect();
    }
  }, {
    key: "_handleReconnect",
    value: function () {
      var _this6 = this;
      if (this.state === CLIENT_STATE.DISCONNECTED) {
        return;
      }
      this.state = CLIENT_STATE.RECONNECTING;
      var time = generateInterval(this.attempts);
      if (this.reconnectHandle) {
        clearTimeout(this.reconnectHandle);
      }
      this.reconnectHandle = setTimeout(function () {
        _this6.attempts++;
        _this6.connectPromise = (0, _promiseUtils.resolvingPromise)();
        _this6.open();
      }.bind(this), time);
    }
  }]);
}();
var _default = exports.default = LiveQueryClient;