import * as plugins from './plugins.js';
import { Collection } from './classes.collection.js';
import { SmartdataDb } from './classes.db.js';
import { SmartDataDbDoc, svDb, unI } from './classes.doc.js';

/**
 * EasyStore allows the storage of easy objects. It also allows easy sharing of the object between different instances
 */
export class EasyStore<T> {
  // instance
  public smartdataDbRef: SmartdataDb;
  public nameId: string;

  private easyStoreClass = (() => {
    @Collection(() => this.smartdataDbRef)
    class SmartdataEasyStore extends SmartDataDbDoc<SmartdataEasyStore, SmartdataEasyStore> {
      @unI()
      public nameId: string;

      @svDb()
      public ephermal: {
        activated: boolean;
        timeout: number;
      };

      @svDb()
      lastEdit: number;

      @svDb()
      public data: Partial<T>;
    }
    return SmartdataEasyStore;
  })();

  constructor(nameIdArg: string, smnartdataDbRefArg: SmartdataDb) {
    this.smartdataDbRef = smnartdataDbRefArg;
    this.nameId = nameIdArg;
  }

  private easyStorePromise: Promise<InstanceType<typeof this.easyStoreClass>>;
  private async getEasyStore(): Promise<InstanceType<typeof this.easyStoreClass>> {
    if (this.easyStorePromise) {
      return this.easyStorePromise;
    }

    // first run from here
    const deferred = plugins.smartpromise.defer<InstanceType<typeof this.easyStoreClass>>();
    this.easyStorePromise = deferred.promise;

    let easyStore = await this.easyStoreClass.getInstance({
      nameId: this.nameId,
    });

    if (!easyStore) {
      easyStore = new this.easyStoreClass();
      easyStore.nameId = this.nameId;
      easyStore.data = {};
      await easyStore.save();
    }
    deferred.resolve(easyStore);
    return this.easyStorePromise;
  }

  /**
   * reads all keyValue pairs at once and returns them
   */
  public async readAll() {
    const easyStore = await this.getEasyStore();
    return easyStore.data;
  }

  /**
   * reads a keyValueFile from disk
   */
  public async readKey(keyArg: keyof T) {
    const easyStore = await this.getEasyStore();
    return easyStore.data[keyArg];
  }

  /**
   * writes a specific key to the keyValueStore
   */
  public async writeKey<TKey extends keyof T>(keyArg: TKey, valueArg: T[TKey]) {
    const easyStore = await this.getEasyStore();
    easyStore.data[keyArg] = valueArg;
    await easyStore.save();
  }

  public async deleteKey(keyArg: keyof T) {
    const easyStore = await this.getEasyStore();
    delete easyStore.data[keyArg];
    await easyStore.save();
  }

  /**
   * writes all keyValue pairs in the object argument
   */
  public async writeAll(keyValueObject: Partial<T>) {
    const easyStore = await this.getEasyStore();
    easyStore.data = { ...easyStore.data, ...keyValueObject };
    await easyStore.save();
  }

  /**
   * wipes a key value store from disk
   */
  public async wipe() {
    const easyStore = await this.getEasyStore();
    easyStore.data = {};
    await easyStore.save();
  }

  public async cleanUpEphermal() {
    while (
      (await this.smartdataDbRef.statusConnectedDeferred.promise) &&
      this.smartdataDbRef.status === 'connected'
    ) {}
  }
}