'use strict';

const TokenProvider = require('./providers/token');
const LeaseManager = require('./lease-manager');
const onceWithTimeout = require('./utils/once-with-timeout');

// Default token secret
const DEFAULT_SECRET = '/v1/token/default';

// Default LeaseManager timeout
const DEFAULT_TIMEOUT_MILLIS = 500;

// LeaseManager status
const STATUS = {
  READY: 'READY',
  PENDING: 'PENDING'
};

class StorageService {

  /**
   * Create a new instance of a StorageService.
   *
   * @param {object} [options] - configuration options
   * @param {string} [options.timeout] - the timeout value (in millis) when looking up secrets
   *
   */
  constructor(options) {
    options = options || {};
    this.timeout = options.timeout || DEFAULT_TIMEOUT_MILLIS;

    this._managers = new Map();
    this._getLeaseManager(DEFAULT_SECRET);
  }

  /**
   * Gets the data for the SecretProvider corresponding to the given secret, passing it to the given
   * callback. If the underlying LeaseManager is not READY within the configured timeout period,
   * the lookup call will timeout and the given callback will passed a timeout error.
   *
   * @param {string} secret - the secret to lookup
   * @param {Function} callback - function of the form callback(err, data)
   *
   */
  lookup(secret, callback) {
    const manager = this._getLeaseManager(secret);

    if (manager.status === STATUS.READY) {

      // LeaseManager is ready, immediately callback with data
      callback(null, manager.data);
    } else {

      // Queue the callback with a timeout
      onceWithTimeout(manager, 'ready', this.timeout)
        .then(() => callback(null, manager.data))
        .catch((err) => callback(err, null));
    }
  }

  /**
   * Gets a LeaseManager for the given secret, or provisions a new LeaseManager
   * if it is not already provisioned.
   *
   * @private
   * @param {String} secret - the secret to get (or provision) a LeaseManager for
   *
   * @returns {LeaseManager}
   *
   */
  _getLeaseManager(secret) {

    // If the LeaseManager is already provisioned, return it
    if(this._managers.has(secret)) {
      return this._managers.get(secret);
    }

    // Otherwise, provision a new LeaseManager for the secret
    // TODO: get provider configuration (options) using secret
    // TODO: determine provider type from secret
    const options = {};
    const provider = new TokenProvider(options);
    const manager = new LeaseManager(provider);

    // Initialize the LeaseManager
    manager.initialize();

    this._managers.set(secret, manager);

    return manager;
  }
}

module.exports = StorageService;

@@ -2447,24 +2447,64 @@
 provider);%0A%0A
+    StorageService.setEvents(manager);%0A%0A
     // Initi
@@ -2618,16 +2618,733 @@
 ger;%0A  %7D
+%0A%0A  /**%0A   * Set basic events on a LeaseManager%0A   * @param %7BLeaseManager%7D manager%0A   */%0A  static setEvents(manager) %7B%0A    const type = manager.provider.constructor.name;%0A%0A    manager.on('ready', () =%3E %7B%0A      Log.log('INFO', 'Manager is ready', %7B%0A        provider: type,%0A        status: manager.status,%0A        data: manager.data,%0A        lease_duration: manager.lease_duration%0A      %7D);%0A    %7D);%0A%0A    manager.on('renewed', () =%3E %7B%0A      Log.log('INFO', 'Manager renewed provider%5C's data', %7B%0A        provider: type,%0A        status: manager.status,%0A        data: manager.data,%0A        lease_duration: manager.lease_duration%0A      %7D);%0A    %7D);%0A%0A    manager.on('error', (err) =%3E %7B%0A      Log.log('ERROR', err);%0A    %7D);%0A  %7D
 %0A%7D%0A%0Amodu

Set event listener on lease-managers for logging.