/**
 * Global class representing the localStorage/sessionStorage functionality,
 * extending it with locally created storage object in the case of not existing localStorage and sessionStorage.
 */
export class AppStorage {
  static appStorage: Object = {};

  /**
   * Returns the current value associated with the given key, or null if the given key does not exist.
   */
  static getItem(key: string): string | null {
    if (localStorage) {
      return localStorage.getItem(key);
    }
    if (sessionStorage) {
      return sessionStorage.getItem(key);
    }

    if (this.appStorage.hasOwnProperty(key)) {
      return this.appStorage[key];
    }

    return null;
  }

  /**
   * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
   *
   * Throws a "QuotaExceededError" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.)
   *
   * Dispatches a storage event on Window objects holding an equivalent Storage object.
   */
  static setItem(key: string, value: string): void {
    if (localStorage) {
      localStorage.setItem(key, value);
      return;
    }
    if (sessionStorage) {
      sessionStorage.setItem(key, value);
      return;
    }

    this.appStorage[key] = value;
    return;
  }

  /**
   * Removes all key/value pairs, if there are any.
   *
   * Dispatches a storage event on Window objects holding an equivalent Storage object.
   */
  static clear(): void {
    if (localStorage) {
      localStorage.clear();
      return;
    }
    if (sessionStorage) {
      sessionStorage.clear();
      return;
    }

    for (const key of Object.keys(this.appStorage)) {
      delete this.appStorage[key];
    }
    return;
  }

  /**
   * Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs.
   */
  static key(index: number): string | null {
    if (localStorage) {
      return localStorage.key(index);
    }
    if (sessionStorage) {
      return sessionStorage.key(index);
    }

    return Object.values(this.appStorage)?.[index] ?? null;
  }

  /**
   * Returns the number of key/value pairs.
   *
   * Works like the AppStorage.length property
   */
  static getLength(): number {
    if (localStorage) {
      return localStorage.length;
    }
    if (sessionStorage) {
      return sessionStorage.length;
    }

    return Object.values(this.appStorage).length;
  }

  /**
   * Removes the key/value pair with the given key, if a key/value pair with the given key exists.
   *
   * Dispatches a storage event on Window objects holding an equivalent Storage object.
   */
  static removeItem(key: string): void {
    if (localStorage) {
      localStorage.removeItem(key);
      return;
    }
    if (sessionStorage) {
      sessionStorage.removeItem(key);
      return;
    }

    if (this.appStorage.hasOwnProperty(key)) {
      delete this.appStorage[key];
    }
    return;
  }

  /**
   * Remove all items which start with the prefix param
   * @param prefix
   */
  static removeItemWithPrefix(prefix: string): void {
    let storage = localStorage || sessionStorage || this.appStorage;

    let keys = Object.keys(storage);

    keys.forEach((k) => {
      if (k.startsWith(prefix)) {
        this.removeItem(k);
      }
    });
  }
}
