'use strict';


const REDIS_CACHE = Symbol('Context#RedisCache');
const NODE_CACHE = Symbol('Context#NodeCache');

const NodeCache = require('node-cache');

class Cache {
  constructor(app) {
    this.app = app;
    this.name = 'unknown-cache';
  }

  async val(key, next, ttl) {
    let data = await this.get(key);
    if (data) {
      return data;
    }
    data = await next(key);
    await this.set(key, data, ttl);
    return data;
  }

  async get(key) {
    const startTime = +new Date();
    const ret = await this._get(key);
    let jsonText = JSON.stringify(ret);
    if (jsonText === undefined) {
      jsonText = 'undefined';
    } else if (jsonText.length >= 128) {
      if (/^\{/.test(jsonText)) {
        jsonText = '{...}';
      } else if (/^\[/.test(jsonText)) {
        jsonText = '[...]';
      } else {
        jsonText = jsonText.substr(0, 125) + '...';
      }
    }
    this.app.logger.info(`[cache](${+new Date() - startTime}ms) ${this.name}.${key}: ${jsonText}`);
    return ret;
  }
  async set(key, value, ttl = 60) {
    return await this._set(key, value, ttl);
  }
}

class RedisCacheWrap extends Cache {
  constructor(app, redis) {
    super(app);
    this.redis = redis;
    this.name = 'redis-cache';
  }

  async _get(key) {
    const { redis } = this;
    let value = await redis.get(key);
    if (value === null) {
      value = undefined;
    }
    if (value) {
      value = JSON.parse(value);
    }
    return value;
  }
  async _set(key, value, ttl) {
    const { redis } = this;
    value = JSON.stringify(value);
    if (ttl > 0) {
      await redis.set(key, value, 'EX', ttl);
    } else {
      await redis.set(key, value);
    }
  }
}

class NodeCacheWrap extends Cache {
  constructor(app) {
    super(app);
    this.cache = new NodeCache();
    this.name = 'node-cache';
  }

  async _get(key) {
    return this.cache.get(key);
  }
  async _set(key, value, ttl) {
    const { cache } = this;
    value = JSON.parse(JSON.stringify(value));
    if (ttl > 0) {
      cache.set(key, value, ttl);
    } else {
      cache.set(key, value);
    }
  }
}

module.exports = {
  get memcache() {
    if (!this[REDIS_CACHE]) {
      this[REDIS_CACHE] = new RedisCacheWrap(this, this.redis);
    }
    return this[REDIS_CACHE];
  },
  get cache() {
    if (!this[NODE_CACHE]) {
      this[NODE_CACHE] = new NodeCacheWrap(this);
    }
    return this[NODE_CACHE];
  },
};

