/***
 * Copyright (c) 2016 - 2022 Alex Grant (@localnerve), LocalNerve LLC
 * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms.
 *
 * A higher level convenience database access wrapper.
 */
/* global Promise */
import debugLib from './debug';
import * as idb from './idb';

const debug = debugLib('db');
const __test__ = __TEST__; // eslint-disable-line no-undef

/**
 * Construct a DataWrapper object.
 * Convenience wrapper for idb access by store and key.
 *
 * @constructor
 *
 * @param {String} storeName - The name of the idb store.
 * @param {Object} options - The creation options.
 * @param {String} options.key - The name of the key to access.
 * @param {Boolean} [options.emulateError] - for dev only, use to force error
 * outcomes in tests.
 */
function DataWrapper (storeName, options) {
  this.storeName = storeName;
  this.keyName = options.key;

  if (__test__) { // deadcode removed 
    if (typeof idb.emulateError === 'function') {
      idb.emulateError(options.emulateError);
    }
  }
}

DataWrapper.prototype = {
  /**
   * Read a value from a store.key.
   * If value is undefined, not found, rejects with informative error.
   *
   * @returns {Promise} Resolves to value on success.
   */
  read: function read () {
    const keyName = this.keyName,
      storeName = this.storeName;

    return idb.get(storeName, keyName).then((value) => {
      return new Promise((resolve, reject) => {
        if (typeof value !== 'undefined') {
          debug(`successfully read ${keyName} from ${storeName}`);
          resolve(value);
        } else {
          const err = new Error(`${keyName} not found in ${storeName}`);
          debug(err.message);
          reject(err);
        }
      });
    });
  },
  /**
   * Update a store.key value.
   *
   * @param {AnySupportedType} value - The new value.
   * @returns {Promise} Resolves or rejects when complete.
   */
  update: function update (value) {
    debug(`updating ${this.keyName} in ${this.storeName}`);
    return idb.put(this.storeName, this.keyName, value);
  }
};

/***
 * There will be one exposed method here for each idb.stores key.
 */
export const stores = {};

/**
 * Create public data access wrappers for the keys in stores.
 * These convenience methods allow the using code to obtain a
 * get/put wrapper by storeName and keyName, exposing read/update methods
 * that just operate on values.
 *
 * Signature: <storeName> (options)
 * storeName from idb.stores is the method name.
 */
Object.keys(idb.stores).forEach((storeKey) => {
  /**
   * Store wrapper method.
   *
   * @param {Object} options - Creation options object.
   * @param {String} options.key - The keyName to operate on.
   * @returns {DataWrapper} A DataWrapper object.
   */
  stores[storeKey] = (options) => {
    return new DataWrapper(idb.stores[storeKey], options);
  };
});
