import { StorageUtil, ExpiredUnit } from '../utils/storage.util';
import { fromEvent, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';

import CryptoJS from 'crypto-js';
import { NGXLogger } from 'ngx-logger';

export class StorageService {
  constructor(private storage: Storage, private logger: NGXLogger) {}

  get<T>(key: string): T | null {
    return StorageUtil.get(this.storage, key);
  }

  set<T>(
    key: string,
    value: T,
    expiredAt: number = 0,
    expiredUnit: ExpiredUnit = 'd'
  ) {
    return StorageUtil.set(this.storage, key, value, expiredAt, expiredUnit);
  }

  encGet<T>(key: string, secretPassphrase: string): T | null {
    const encrypted = StorageUtil.get(this.storage, key) as CryptoJS.WordArray;
    if (!encrypted) {
      return null;
    }
    const decrypted = CryptoJS.AES.decrypt(encrypted, secretPassphrase);
    const json = decrypted.toString(CryptoJS.enc.Utf8);

    return JSON.parse(json) as T;
  }

  encSet<T>(
    key: string,
    value: T,
    secretPassphrase: string,
    expiredAt: number = 0,
    expiredUnit: ExpiredUnit = 'd'
  ) {
    this.logger.debug('encSet key:', key, ' value:', value);
    const json = JSON.stringify(value);
    const encrypted = CryptoJS.AES.encrypt(json, secretPassphrase);
    return StorageUtil.set(
      this.storage,
      key,
      encrypted.toString(),
      expiredAt,
      expiredUnit
    );
  }

  remove(key: string | RegExp) {
    if (typeof key === 'string') {
      StorageUtil.remove(this.storage, key);
      return;
    }
    let index = 0;
    let next = StorageUtil.key(this.storage, index);
    const ls: string[] = [];
    while (next) {
      if (key.test(next)) {
        ls.push(next);
      }
      next = StorageUtil.key(this.storage, ++index);
    }
    ls.forEach(v => StorageUtil.remove(this.storage, v));
  }

  clear() {
    this.storage.clear();
  }

  observe(key: string): Observable<StorageEvent> {
    return fromEvent<StorageEvent>(window, 'storage').pipe(
      filter(event => event.storageArea === this.storage),
      filter(event => event.key === key)
    );
  }
}
