const DEFAULT_CACHE_TTL = 6 * 60 * 60 * 1000; // 6 hours

/**
 * A cache object keyed on the stringified function arguments.
 */
interface Cache {
  [args: string]: {
    readonly data: any;
    readonly lastUpdate: number;
  };
}

/**
 * Define a function wrapper that locally caches the return value for the given
 * arguments.
 *
 * @param func The function to memoize.
 * @param ttl The length of the time the data stored in the cache is valid.
 */
export function memoize<T extends WrappableFunction>(
  func: T,
  ttl: number = DEFAULT_CACHE_TTL,
): T {
  let cache: Cache = {};
  return <T>function(...args: any[]): any {
    const now = new Date().valueOf();
    const key = JSON.stringify(args);
    const isCacheValid =
      key in cache &&
      cache[key].data !== undefined &&
      now - ttl < cache[key].lastUpdate;
    if (isCacheValid) {
      return cache[key].data;
    }
    const ret = func(...args);
    cache[key] = {
      data: ret,
      lastUpdate: now,
    };
    return ret;
  };
}
