'use strict';

module.exports = function (opts) {
	const LRU = require('lru-cache');
	const pubsub = require('./pubsub');

	const cache = new LRU(opts);

	cache.name = opts.name;
	cache.hits = 0;
	cache.misses = 0;
	cache.enabled = opts.hasOwnProperty('enabled') ? opts.enabled : true;

	const cacheSet = cache.set;
	const cacheGet = cache.get;
	const cacheDel = cache.del;
	const cacheReset = cache.reset;

	cache.set = function (key, value, maxAge) {
		if (!cache.enabled) {
			return;
		}
		cacheSet.apply(cache, [key, value, maxAge]);
	};

	cache.get = function (key) {
		if (!cache.enabled) {
			return undefined;
		}
		const data = cacheGet.apply(cache, [key]);
		if (data === undefined) {
			cache.misses += 1;
		} else {
			cache.hits += 1;
		}
		return data;
	};

	cache.del = function (keys) {
		if (!Array.isArray(keys)) {
			keys = [keys];
		}
		pubsub.publish(`${cache.name}:cache:del`, keys);
		keys.forEach(key => cacheDel.apply(cache, [key]));
	};

	cache.reset = function () {
		pubsub.publish(`${cache.name}:cache:reset`);
		localReset();
	};

	function localReset() {
		cacheReset.apply(cache);
		cache.hits = 0;
		cache.misses = 0;
	}

	pubsub.on(`${cache.name}:cache:reset`, () => {
		localReset();
	});

	pubsub.on(`${cache.name}:cache:del`, (keys) => {
		if (Array.isArray(keys)) {
			keys.forEach(key => cacheDel.apply(cache, [key]));
		}
	});

	cache.getUnCachedKeys = function (keys, cachedData) {
		if (!cache.enabled) {
			return keys;
		}
		let data;
		let isCached;
		const unCachedKeys = keys.filter((key) => {
			data = cache.get(key);
			isCached = data !== undefined;
			if (isCached) {
				cachedData[key] = data;
			}
			return !isCached;
		});

		const hits = keys.length - unCachedKeys.length;
		const misses = keys.length - hits;
		cache.hits += hits;
		cache.misses += misses;
		return unCachedKeys;
	};

	return cache;
};