"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));

/** !
 * koa-redis - index.js
 * Copyright(c) 2015
 * MIT Licensed
 *
 * Authors:
 *   dead_horse <dead_horse@qq.com> (http://deadhorse.me)
 */

/**
 * Module dependencies.
 */
var util = require('util');

var _require = require('events'),
    EventEmitter = _require.EventEmitter;

var debug = require('debug')('koa-redis');

var Redis = require('ioredis');

var wrap = require('co-wrap-all');
/**
 * Initialize redis session middleware with `opts` (see the README for more info):
 *
 * @param {Object} options
 *   - {Boolean} isRedisCluster redis is cluster
 *   - {Object} client       redis client (overides all other options except db and duplicate)
 *   - {String} socket       redis connect socket (DEPRECATED: use 'path' instead)
 *   - {String} db           redis db
 *   - {Boolean} duplicate   if own client object, will use node redis's duplicate function and pass other options
 *   - {String} password     redis password
 *   - {Any} [any]           all other options including above are passed to ioredis
 * @returns {Object} Redis instance
 */


function RedisStore(options) {
  var _this = this;

  if (!(this instanceof RedisStore)) {
    return new RedisStore(options);
  }

  EventEmitter.call(this);
  options = options || {};
  var client;
  options.password = options.password || options.auth_pass || options.pass || null; // For backwards compatibility

  options.path = options.path || options.socket || null; // For backwards compatibility

  if (!options.client) {
    //
    // TODO: we should probably omit custom options we have
    // in this lib from `options` passed to instances below
    //
    var redisUrl = options.url && options.url.toString();
    delete options.url;

    if (options.isRedisCluster) {
      debug('Initializing Redis Cluster');
      delete options.isRedisCluster;
      client = new Redis.Cluster(options.nodes, options.clusterOptions);
    } else {
      debug('Initializing Redis');
      delete options.isRedisCluster;
      delete options.nodes;
      delete options.clusterOptions;
      client = redisUrl ? new Redis(redisUrl, options) : new Redis(options);
    }
  } else if (options.duplicate) {
    // Duplicate client and update with options provided
    debug('Duplicating provided client with new options (if provided)');
    var dupClient = options.client;
    delete options.client;
    delete options.duplicate;
    client = dupClient.duplicate(options); // Useful if you want to use the DB option without adjusting the client DB outside koa-redis
  } else {
    debug('Using provided client');
    client = options.client;
  }

  if (options.db) {
    debug('selecting db %s', options.db);
    client.select(options.db);
    client.on('connect', function () {
      client.send_anyways = true;
      client.select(options.db);
      client.send_anyways = false;
    });
  }

  ['connect', 'ready', 'error', 'close', 'reconnecting', 'end'].forEach(function (name) {
    _this.on(name, function () {
      return debug(`redis ${name}`);
    });

    client.on(name, _this.emit.bind(_this, name));
  }); // For backwards compatibility

  client.on('end', this.emit.bind(this, 'disconnect'));
  this.client = client;
  Object.defineProperty(this, 'status', {
    get() {
      return this.client.status;
    }

  });
  Object.defineProperty(this, 'connected', {
    get() {
      return ['connect', 'ready'].includes(this.status);
    }

  }); // Support optional serialize and unserialize

  this.serialize = typeof options.serialize === 'function' && options.serialize || JSON.stringify;
  this.unserialize = typeof options.unserialize === 'function' && options.unserialize || JSON.parse;
}

util.inherits(RedisStore, EventEmitter);
RedisStore.prototype.get =
/*#__PURE__*/
_regenerator.default.mark(function _callee(sid) {
  var data;
  return _regenerator.default.wrap(function _callee$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return this.client.get(sid);

        case 2:
          data = _context.sent;
          debug('get session: %s', data || 'none');

          if (data) {
            _context.next = 6;
            break;
          }

          return _context.abrupt("return", null);

        case 6:
          _context.prev = 6;
          return _context.abrupt("return", this.unserialize(data.toString()));

        case 10:
          _context.prev = 10;
          _context.t0 = _context["catch"](6);
          // ignore err
          debug('parse session error: %s', _context.t0.message);

        case 13:
        case "end":
          return _context.stop();
      }
    }
  }, _callee, this, [[6, 10]]);
});
RedisStore.prototype.set =
/*#__PURE__*/
_regenerator.default.mark(function _callee2(sid, sess, ttl) {
  return _regenerator.default.wrap(function _callee2$(_context2) {
    while (1) {
      switch (_context2.prev = _context2.next) {
        case 0:
          if (typeof ttl === 'number') {
            ttl = Math.ceil(ttl / 1000);
          }

          sess = this.serialize(sess);

          if (!ttl) {
            _context2.next = 8;
            break;
          }

          debug('SETEX %s %s %s', sid, ttl, sess);
          _context2.next = 6;
          return this.client.setex(sid, ttl, sess);

        case 6:
          _context2.next = 11;
          break;

        case 8:
          debug('SET %s %s', sid, sess);
          _context2.next = 11;
          return this.client.set(sid, sess);

        case 11:
          debug('SET %s complete', sid);

        case 12:
        case "end":
          return _context2.stop();
      }
    }
  }, _callee2, this);
});
RedisStore.prototype.destroy =
/*#__PURE__*/
_regenerator.default.mark(function _callee3(sid) {
  return _regenerator.default.wrap(function _callee3$(_context3) {
    while (1) {
      switch (_context3.prev = _context3.next) {
        case 0:
          debug('DEL %s', sid);
          _context3.next = 3;
          return this.client.del(sid);

        case 3:
          debug('DEL %s complete', sid);

        case 4:
        case "end":
          return _context3.stop();
      }
    }
  }, _callee3, this);
});
RedisStore.prototype.quit =
/*#__PURE__*/
_regenerator.default.mark(function _callee4() {
  return _regenerator.default.wrap(function _callee4$(_context4) {
    while (1) {
      switch (_context4.prev = _context4.next) {
        case 0:
          // End connection SAFELY
          debug('quitting redis client');
          _context4.next = 3;
          return this.client.quit();

        case 3:
        case "end":
          return _context4.stop();
      }
    }
  }, _callee4, this);
});
wrap(RedisStore.prototype);
RedisStore.prototype.end = RedisStore.prototype.quit; // End connection SAFELY. The real end() command should never be used, as it cuts off to queue.

module.exports = RedisStore;