'use strict';

var _helpers = require('../database/helpers');

var _helpers2 = _interopRequireDefault(_helpers);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * The Dialog State Manager is in charge of keeping track of the state
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * for all conversations. This is being used internally by the [Dialog Engine]{@link DialogEngine}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * but is also exposed publicly if you need to programmatically alter the state of some conversations.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * @namespace DialogStateManager
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * @example
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * bp.dialogEngine.stateManager
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            */

module.exports = ({ db, internals = {} }) => {
  const _internals = Object.assign({
    _isExpired: session => {
      return false; // TODO Implement
    }
  }, internals);

  const _upsertState = (() => {
    var _ref = _asyncToGenerator(function* (stateId, state) {
      const knex = yield db.get();

      const params = {
        tableName: 'dialog_sessions',
        stateId,
        state: JSON.stringify(state),
        now: (0, _helpers2.default)(knex).date.now()
      };

      const sql = (0, _helpers2.default)(knex).isLite() ? `
        INSERT OR REPLACE INTO :tableName: (id, state, active_on)
        VALUES (:stateId, :state, :now)
      ` : `
        INSERT INTO :tableName: (id, state, active_on, created_on)
        VALUES (:stateId, :state, :now, :now)
        ON CONFLICT (id) DO UPDATE
          SET active_on = :now, state = :state
      `;

      return knex.raw(sql, params);
    });

    return function _upsertState(_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })();

  const _createEmptyState = stateId => {
    return { _stateId: stateId };
  };

  const _createSession = (() => {
    var _ref2 = _asyncToGenerator(function* (stateId) {
      const knex = yield db.get();

      const state = _createEmptyState(stateId);

      const params = {
        tableName: 'dialog_sessions',
        stateId,
        state: JSON.stringify(state),
        now: (0, _helpers2.default)(knex).date.now()
      };

      const sql = (0, _helpers2.default)(knex).isLite() ? `
        INSERT OR REPLACE INTO :tableName: (id, state, active_on, created_on)
        VALUES (:stateId, :state, :now, :now)
      ` : `
        INSERT INTO :tableName: (id, state, active_on, created_on)
        VALUES (:stateId, :state, :now, :now)
        ON CONFLICT (id) DO UPDATE
          SET created_on = :now, active_on = :now, state = :state
      `;

      yield knex.raw(sql, params);

      return state;
    });

    return function _createSession(_x3) {
      return _ref2.apply(this, arguments);
    };
  })();

  /**
   * Returns the current state of the conversation
   * @param  {String} stateId
   * @return {Object} The conversation state
   * @async
   * @memberof! DialogStateManager
   * @example
   * const state = await bp.dialogEngine.stateManager.getState(event.user.id)
   */
  const getState = (() => {
    var _ref3 = _asyncToGenerator(function* (stateId) {
      const knex = yield db.get();

      const session = yield knex('dialog_sessions').where({ id: stateId }).limit(1).then().get(0).then();

      if (session) {
        if (_internals._isExpired(session)) {
          // TODO trigger time out
          return _createSession(stateId);
        } else {
          return JSON.parse(session.state);
        }
      } else {
        return _createSession(stateId);
      }
    });

    return function getState(_x4) {
      return _ref3.apply(this, arguments);
    };
  })();

  /**
   * Overwrites the state of a current conversation
   * @param  {String} stateId
   * @param {Object} state The conversation state
   * @return {Object} The new state
   * @async
   * @memberof! DialogStateManager
   */
  const setState = (stateId, state) => {
    if (_lodash2.default.isNil(state)) {
      state = _createEmptyState(stateId);
    }

    if (!_lodash2.default.isPlainObject(state)) {
      throw new Error('State must be a plain object');
    }

    return _upsertState(stateId, state);
  };

  /**
   * Deletes the state(s) and (optionally) the associated sub-states (for e.g. ___context sub-state)
   * @param stateId The state to delete
   * @param {Array<String>} [substates] Detaults to ['context']. If this is empty it will delete no substate
   * @async
   * @memberof! DialogStateManager
   */
  const deleteState = (() => {
    var _ref4 = _asyncToGenerator(function* (stateId, substates = ['context']) {
      const knex = yield db.get();

      const states = [stateId, ...substates.map(function (x) {
        return `${stateId}___${x}`;
      })];

      yield knex('dialog_sessions').whereIn('id', states).del().then();
    });

    return function deleteState(_x5) {
      return _ref4.apply(this, arguments);
    };
  })();

  return {
    getState,
    setState,
    deleteState
  };
};
//# sourceMappingURL=state.js.map