import { Injectable } from '@angular/core';
import { CacheOptions } from './cache-options';
import { Cached } from './cached';
import { sha1 } from 'object-hash';

@Injectable({
  providedIn: 'root',
})
export class CacheService {
  constructor() {}

  set(token: Object, data: any, options?: CacheOptions) {
    const persist = options?.persist;
    const ttl = options?.ttl;
    const cached: Cached = {
      data,
    };

    if (ttl) {
      cached.until = Date.now() + ttl;
    }

    if (persist) {
      localStorage.setItem(this.getKey(token), this.serialize(cached));
    } else {
      sessionStorage.setItem(this.getKey(token), this.serialize(cached));
    }

    Object.freeze(cached);
    return cached;
  }

  get<T = any>(token: Object, from: 'session' | 'persist' = 'session'): T {
    let cached: Cached;

    if (from === 'session') {
      cached = this.deserialize(sessionStorage.getItem(this.getKey(token)));
    } else if (from === 'persist') {
      cached = this.deserialize(localStorage.getItem(this.getKey(token)));
    }

    if (!cached) {
      return undefined;
    }

    if (cached.until < Date.now()) {
      this.delete(token, from);
      return undefined;
    }

    return cached.data;
  }

  delete(token: Object, from: 'session' | 'persist' = 'session') {
    if (from === 'session') {
      sessionStorage.removeItem(this.getKey(token));
    } else if (from === 'persist') {
      localStorage.removeItem(this.getKey(token));
    }
  }

  private getKey(token: Object) {
    return sha1(token);
  }

  private serialize(data: Cached): string {
    return JSON.stringify(data);
  }

  private deserialize(data: string): Cached {
    return JSON.parse(data);
  }
}
