/**
 * The fork of
 *   https://gist.github.com/pilbot/9d0567ef1daf556449fb,
 *   https://gist.github.com/glade-at-gigwell/4e080771d685fbf1908edbd98eb2d88c
 */

/**
 * Using the Google Apps Script Cache Service for objects above 100Kb
 */
class ChunkyCache {
  /**
   *
   * @param {GoogleAppsScript.Cache.Cache} cache
   */
  constructor(cache) {
    this.cache = cache || CacheService.getScriptCache();
    this.chunkSize = 100 * 1024;
  }
  /**
   * Gets the cached value for the given key, or null if none is found.
   * https://developers.google.com/apps-script/reference/cache/cache#getkey
   *
   * @param {string} key
   * @returns {any} A JSON.parse result
   */
  get(key) {
    const superKeyjson = this.cache.get(key);
    if (superKeyjson === null) return null;
    const superKey = JSON.parse(superKeyjson);
    const cache = this.cache.getAll(superKey.chunks);
    const chunks = superKey.chunks.map((key) => cache[key]);
    if (
      chunks.every(function (c) {
        return c !== undefined;
      })
    ) {
      return JSON.parse(chunks.join(''));
    }
  }
  /**
   * Adds a key/value pair to the cache.
   * https://developers.google.com/apps-script/reference/cache/cache#putkey,-value
   *
   * @param {string} key
   * @param {string} value
   * @param {number} expirationInSeconds
   */
  put(key, value, expirationInSeconds = 600) {
    const json = JSON.stringify(value);
    const chunks = [];
    const chunkValues = {};
    let index = 0;
    while (index < json.length) {
      const chunkKey = key + '_' + index;
      chunks.push(chunkKey);
      chunkValues[chunkKey] = json.substr(index, this.chunkSize);
      index += this.chunkSize;
    }
    const superKey = {
      chunkSize: this.chunkSize,
      chunks: chunks,
      length: json.length,
    };
    chunkValues[key] = JSON.stringify(superKey);
    this.cache.putAll(chunkValues, expirationInSeconds);
  }
  /**
   * Removes an entry from the cache using the given key.
   *
   * @returns {null}
   */
  remove(key) {
    const superKeyjson = this.cache.get(key);
    if (superKeyjson !== null) {
      const superKey = JSON.parse(superKeyjson);
      this.cache.removeAll([...superKey.chunks, key]);
    }
    return null;
  }
}

/**
 * Using the Google Apps Script Cache Service for blobs
 */
class BlobCache extends ChunkyCache {
  /**
   * Extends of ChunkyCache
   */
  constructor(cache) {
    super(cache);
    this.splitter = '1c16c2eb-a4a7-4cac-bf79-064cedfbb346';
    this.defaultName = '772fff0c-4207-4893-834c-aec73c498eeb';
    this.prefixSize = 250;
  }
  /**
   * Adds a key/blob pair to the cache.
   *
   * @param {string} key
   * @param {GoogleAppsScript.Base.Blob | GoogleAppsScript.Base.BlobSource} blob
   * @param {number} expirationInSeconds
   */
  putBlob(key, blob, expirationInSeconds = 600) {
    let name = blob.getName();
    if (name === null) name = this.defaultName;
    const contentType = blob.getContentType();
    const prefix = [name, this.splitter, contentType, this.splitter]
      .join('')
      .padEnd(this.prefixSize, ' ');
    const data = prefix + Utilities.base64Encode(blob.getBytes());
    this.put(key, data, expirationInSeconds);
  }
  /**
   * Gets the cached blob for the given key, or null if none is found.
   *
   * @param {*} key
   */
  getBlob(key) {
    const data = this.get(key);
    if (data !== null) {
      const prefix = data.slice(0, this.prefixSize).split(this.splitter);
      const blob = Utilities.newBlob('');
      blob.setBytes(Utilities.base64Decode(data.slice(this.prefixSize)));
      if (prefix[0] !== this.defaultName) blob.setName(prefix[0]);
      blob.setContentType(prefix[2]);
      return blob;
    }
    return null;
  }
}