"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.api = api;
var _socketclusterClient = _interopRequireDefault(require("socketcluster-client"));
var _jsan = require("jsan");
var actions = _interopRequireWildcard(require("../constants/socketActionTypes"));
var _instances = require("../reducers/instances");
var _actionTypes = require("../constants/actionTypes");
var _actions = require("../actions");
var _monitorActions = require("../utils/monitorActions");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
let socket;
let store;
function emit(_ref) {
  let {
    message: type,
    id,
    instanceId,
    action,
    state
  } = _ref;
  void socket.transmit(id ? `sc-${id}` : 'respond', {
    type,
    action,
    state,
    instanceId
  });
}
function startMonitoring(channel) {
  if (channel !== store.getState().socket.baseChannel) return;
  store.dispatch({
    type: actions.EMIT,
    message: 'START'
  });
}
function dispatchRemoteAction(_ref2) {
  let {
    message,
    action,
    state,
    toAll
  } = _ref2;
  const instances = store.getState().instances;
  const instanceId = (0, _instances.getActiveInstance)(instances);
  const id = !toAll && instances.options[instanceId].connectionId;
  store.dispatch({
    type: actions.EMIT,
    message,
    action,
    state: (0, _monitorActions.nonReduxDispatch)(store, message, instanceId, action, state, instances),
    instanceId,
    id
  });
}
function monitoring(request) {
  if (request.type === 'DISCONNECTED') {
    store.dispatch({
      type: _actionTypes.REMOVE_INSTANCE,
      id: request.id
    });
    return;
  }
  if (request.type === 'START') {
    store.dispatch({
      type: actions.EMIT,
      message: 'START',
      id: request.id
    });
    return;
  }
  if (request.type === 'ERROR') {
    store.dispatch((0, _actions.showNotification)(request.payload));
    return;
  }
  store.dispatch({
    type: _actionTypes.UPDATE_STATE,
    request: request.data ? {
      ...request.data,
      id: request.id
    } : request
  });
  const instances = store.getState().instances;
  const instanceId = request.instanceId || request.id;
  if (instances.sync && instanceId === instances.selected && (request.type === 'ACTION' || request.type === 'STATE')) {
    void socket.transmit('respond', {
      type: 'SYNC',
      state: (0, _jsan.stringify)(instances.states[instanceId]),
      id: request.id,
      instanceId
    });
  }
}
function subscribe(channelName, subscription) {
  const channel = socket.subscribe(channelName);
  if (subscription === _actionTypes.UPDATE_STATE) {
    void (async () => {
      for await (const data of channel) {
        monitoring(data);
      }
    })();
  } else {
    const watcher = request => {
      store.dispatch({
        type: subscription,
        request
      });
    };
    void (async () => {
      for await (const data of channel) {
        watcher(data);
      }
    })();
  }
}
function handleConnection() {
  void (async () => {
    for await (const data of socket.listener('connect')) {
      store.dispatch({
        type: actions.CONNECT_SUCCESS,
        payload: {
          id: data.id,
          authState: socket.authState,
          socketState: socket.state
        },
        // @ts-expect-error Is this legitimate?
        error: data.authError
      });
      if (socket.authState !== actions.AUTHENTICATED) {
        store.dispatch({
          type: actions.AUTH_REQUEST
        });
      }
    }
  })();
  void (async () => {
    for await (const data of socket.listener('disconnect')) {
      store.dispatch({
        type: actions.DISCONNECTED,
        code: data.code
      });
    }
  })();
  void (async () => {
    for await (const data of socket.listener('subscribe')) {
      store.dispatch({
        type: actions.SUBSCRIBE_SUCCESS,
        channel: data.channel
      });
    }
  })();
  void (async () => {
    for await (const data of socket.listener('unsubscribe')) {
      void socket.unsubscribe(data.channel);
      store.dispatch({
        type: actions.UNSUBSCRIBE,
        channel: data.channel
      });
    }
  })();
  void (async () => {
    for await (const data of socket.listener('subscribeFail')) {
      store.dispatch({
        type: actions.SUBSCRIBE_ERROR,
        error: data.error,
        status: 'subscribeFail'
      });
    }
  })();
  void (async () => {
    for await (const data of socket.listener('error')) {
      store.dispatch({
        type: actions.CONNECT_ERROR,
        error: data.error
      });
    }
  })();
}
function connect() {
  if (process.env.NODE_ENV === 'test') return;
  const connection = store.getState().connection;
  try {
    socket = _socketclusterClient.default.create(connection.options);
    handleConnection();
  } catch (error) {
    store.dispatch({
      type: actions.CONNECT_ERROR,
      error: error
    });
    store.dispatch((0, _actions.showNotification)(error.message || error));
  }
}
function disconnect() {
  if (socket) {
    socket.disconnect();
  }
}
function login() {
  void (async () => {
    try {
      const baseChannel = await socket.invoke('login', {});
      store.dispatch({
        type: actions.AUTH_SUCCESS,
        baseChannel
      });
      store.dispatch({
        type: actions.SUBSCRIBE_REQUEST,
        channel: baseChannel,
        subscription: _actionTypes.UPDATE_STATE
      });
      store.dispatch({
        type: actions.SUBSCRIBE_REQUEST,
        channel: 'report',
        subscription: _actionTypes.UPDATE_REPORTS
      });
    } catch (error) {
      store.dispatch({
        type: actions.AUTH_ERROR,
        error: error
      });
    }
  })();
}
function getReport(reportId) {
  void (async () => {
    try {
      const data = await socket.invoke('getReport', reportId);
      store.dispatch({
        type: _actionTypes.GET_REPORT_SUCCESS,
        data
      });
      store.dispatch((0, _actions.importState)(data.payload));
    } catch (error) {
      store.dispatch({
        type: _actionTypes.GET_REPORT_ERROR,
        error: error
      });
    }
  })();
}
function api(inStore) {
  store = inStore;
  return next => action => {
    const result = next(action);
    switch (action.type) {
      case actions.CONNECT_REQUEST:
        connect();
        break;
      case actions.RECONNECT:
        disconnect();
        if (action.options.type !== 'disabled') connect();
        break;
      case actions.AUTH_REQUEST:
        login();
        break;
      case actions.SUBSCRIBE_REQUEST:
        subscribe(action.channel, action.subscription);
        break;
      case actions.SUBSCRIBE_SUCCESS:
        startMonitoring(action.channel);
        break;
      case actions.EMIT:
        if (socket) emit(action);
        break;
      case _actionTypes.LIFTED_ACTION:
        dispatchRemoteAction(action);
        break;
      case _actionTypes.GET_REPORT_REQUEST:
        getReport(action.report);
        break;
    }
    return result;
  };
}