import localForage from 'localforage';
import crypto from 'crypto-js';
import lodash from 'lodash';

export const setupCache = (cacheKey, ttlDefault = 0, cryptKey = '') => {
  if (!cacheKey) {
    throw new Error('Cache Root is required');
  }

  const storeCache = localForage.createInstance({
    name: lodash.snakeCase(`cache_${cacheKey}`),
  });

  class CacheStore {
    static storeCache = storeCache;

    static async destroy() {
      return storeCache.clear();
    }

    /**
     *
     * @param {string} key
     */
    constructor(key = 'default') {
      if (!key) {
        throw new Error('Cache key is required');
      }

      this.raw = key;
      this.key = crypto.SHA256(key).toString();
    }

    async get() {
      const now = new Date().getTime();
      const getValue = await storeCache.getItem(this.key);

      if (!(getValue && getValue.value)) {
        return null;
      }

      if (getValue.ttl && getValue.ttl < now) {
        this.remove();
        return null;
      }

      let value = getValue.value;

      if (cryptKey) {
        try {
          const bytes = crypto.AES.decrypt(value, cryptKey);
          const decrypted = bytes.toString(crypto.enc.Utf8);

          if (decrypted) {
            value = decrypted;
          }
        } catch (error) {
          return null;
        }
      }

      return JSON.parse(value);
    }

    async set(value) {
      const set = {
        ttl: ttlDefault ? new Date().getTime() + ttlDefault * 1000 : 0,
        value: '',
      };

      let stringValue = JSON.stringify(value);

      if (cryptKey) {
        stringValue = crypto.AES.encrypt(stringValue, cryptKey).toString();
      }

      set.value = stringValue;

      await storeCache.setItem(this.key, set);

      return set;
    }

    async remove() {
      return storeCache.removeItem(this.key);
    }

    getCache = this.get.bind(this);

    setCache = this.set.bind(this);

    removeCache = this.remove.bind(this);
  }

  return CacheStore;
};
