import { Timestamped } from '../models/timestamped';
import { TimestampIsTimeouted } from './timestamp-is-timeouted';
import { TimestampNowFill } from './timestamp-now-fill';

export class MemoryCache<K, T> {
  private memoryCache = {};

  constructor(private timeout: number, private autoclean: number = 0) {
    if (autoclean > 0) {
      this.startAutoclean();
    }
  }

  get(key: K): (T & Timestamped) | null {
    const k = key as any;
    if (this.memoryCache[k]) {
      if (TimestampIsTimeouted(this.memoryCache[k], this.timeout)) {
        delete this.memoryCache[k];
        return null;
      }
      return this.memoryCache[k];
    }
    return null;
  }

  set(key: K, value: T): T & Timestamped {
    this.memoryCache[key as any] = TimestampNowFill({ ...value });
    return this.memoryCache[key as any];
  }

  cleanTimeouted(): void {
    for (const key in this.memoryCache) {
      if (Object.prototype.hasOwnProperty.call(this.memoryCache, key)) {
        const item = this.memoryCache[key];
        if (TimestampIsTimeouted(item, this.timeout)) {
          delete this.memoryCache[key];
        }
      }
    }
  }

  find(fn: (item: T, key: K) => boolean): (T & Timestamped) | null {
    for (const key in this.memoryCache) {
      if (Object.prototype.hasOwnProperty.call(this.memoryCache, key)) {
        const item = this.memoryCache[key];
        if (TimestampIsTimeouted(item, this.timeout)) {
          delete this.memoryCache[key];
          continue;
        }
        if (fn(item, key as any)) {
          return item;
        }
      }
    }
    return null;
  }

  private startAutoclean() {
    setTimeout(() => {
      this.cleanTimeouted();
      this.startAutoclean();
    }, this.autoclean);
  }
}
