import _ from 'lodash';
import { v4 } from 'uuid';

const memorizeCache: { ttl: number; result: any; args: any[]; id: string }[] = [];

export function memorize(options?: { ttl?: number; comparer?: (current: any[], prev: any[]) => boolean }): MethodDecorator {
  const id = v4();
  const comparer = options?.comparer || _.isEqual;
  const ttl = options?.ttl || Infinity;
  return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
    const currentMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
      const cacheIndex = memorizeCache.findIndex((c) => c.id === id && comparer(c.args, args));
      const cache = memorizeCache[cacheIndex];
      if (cache && cache.ttl > Date.now()) {
        return cache.result;
      } else if (cache) {
        memorizeCache.splice(cacheIndex, 1);
      }
      const result = currentMethod.apply(this, args);
      memorizeCache.push({ ttl: Date.now() + ttl, result, args, id });
      return result;
    };
    return descriptor;
  };
}
