text: 'use strict';
  
  const BbPromise = require('bluebird');
  const config = require('@serverless/utils/config');
  const ServerlessError = require('../serverless-error');
  const cliCommandsSchema = require('../cli/commands-schema');
  const muteConsoleLog = require('../utils/log/muteConsoleLog');
  const tabtabOptions = require('../utils/tabCompletion/tabtabOptions');
  const isNpmPackageWritable = require('../utils/npmPackage/isWritable');
  const isNpmGlobalPackage = require('../utils/npmPackage/isGlobal');
  const renderCommandHelp = require('../cli/render-help/command');
  
  // class wide constants
  const validProviders = new Set(['aws']);
  const validShells = new Set(['bash', 'zsh', 'fish']);
  
  // TODO: update to look like the list in the "create" plugin
  // once more than one provider is supported
  const humanReadableProvidersList = `"${Array.from(validProviders)}"`;
  
  const isWindows = process.platform === 'win32';
  const noSupportErrorMessage = isWindows
    ? 'Auto update can only be set for "serverless" installed by npm globally'
    : 'Auto update can only be set for "serverless" installed by npm globally or ' +
      'standalone installation';
  
  class Config {
    constructor(serverless, options) {
      this.serverless = serverless;
      this.options = options;
  
      this.commands = {
        config: {
          ...cliCommandsSchema.get('config'),
          commands: {
            credentials: {
              // Command defined in AWS context
              validProviders,
            },
          },
        },
      };
  
      this.commands.config.commands.tabcompletion = {
        type: 'container',
        commands: {
          install: {
            ...cliCommandsSchema.get('config tabcompletion install'),
          },
          uninstall: {
            ...cliCommandsSchema.get('config tabcompletion uninstall'),
          },
        },
      };
  
      this.hooks = {
        'config:config': async () => this.updateConfig(),
        'before:config:credentials:config': () => this.validate(),
        'config:tabcompletion:install:install': async () => this.tabtabCompletionInstall(),
        'config:tabcompletion:uninstall:uninstall': async () => this.tabtabCompletionUninstall(),
      };
    }
  
    validate() {
      const provider = this.options.provider.toLowerCase();
  
      if (!validProviders.has(provider)) {
        const errorMessage = [
          `Provider "${provider}" is not supported.`,
          ` Supported providers are: ${humanReadableProvidersList}.`,
        ].join('');
        throw new ServerlessError(errorMessage);
      }
    }
  
    async updateConfig() {
      if (this.options.autoupdate == null) {
        renderCommandHelp(this.serverless.processedInput.commands.join(' '));
        return;
      }
      if (this.options.autoupdate) {
        if (config.get('autoUpdate.enabled')) {
          this.serverless.cli.log('Auto update is already turned on');
          return;
        }
        if (this.serverless.isLocallyInstalled) {
          throw new ServerlessError(noSupportErrorMessage);
        }
        if (this.serverless.isStandaloneExecutable) {
          if (process.platform === 'win32') {
            throw new ServerlessError(noSupportErrorMessage);
          }
        } else {
          if (!(await isNpmGlobalPackage())) {
            throw new ServerlessError(noSupportErrorMessage);
          }
          if (!(await isNpmPackageWritable(this.serverless))) {
            throw new ServerlessError(
              'Auto update cannot be set, due to missing write access to npm global installation'
            );
          }
        }
        config.set('autoUpdate.enabled', true);
  
        this.serverless.cli.log(
          'Auto update successfully turned on (Turn off at any time with "serverless config --no-autoupdate")'
        );
      } else {
        if (!config.get('autoUpdate.enabled')) {
          this.serverless.cli.log('Auto update is already turned off');
          return;
        }
        config.set('autoUpdate.enabled', false);
        this.serverless.cli.log(
          'Auto update successfully turned off (Turn on at any time with "serverless config --autoupdate")'
        );
      }
    }
  
    async tabtabCompletionInstall() {
      return BbPromise.try(() => {
        const shell = this.serverless.processedInput.options.shell || 'bash';
  
        if (!validShells.has(shell)) {
          throw new ServerlessError(
            `Shell "${shell}" is not supported. Supported shells: ${Array.from(validShells)}.`
          );
        }
        const location = (() => {
          if (this.serverless.processedInput.options.location) {
            return this.serverless.processedInput.options.location;
          }
          const { BASH_LOCATION, FISH_LOCATION, ZSH_LOCATION } = require('tabtab/lib/constants');
          switch (shell) {
            case 'bash':
              return BASH_LOCATION;
            case 'zsh':
              return ZSH_LOCATION;
            case 'fish':
              return FISH_LOCATION;
            default:
              throw new Error('Unexpected shell choice');
          }
        })();
        const { install } = require('tabtab/lib/installer');
        return muteConsoleLog(() =>
          tabtabOptions.reduce(
            (previousOperation, options) =>
              previousOperation.then(() => install(Object.assign({ location }, options))),
            BbPromise.resolve()
          )
        ).then(() =>
          this.serverless.cli.log(
            `Tab autocompletion setup for ${shell}. Make sure to reload your SHELL.`
          )
        );
      });
    }
  
    async tabtabCompletionUninstall() {
      return BbPromise.try(() => {
        const { uninstall } = require('tabtab/lib/installer');
        return muteConsoleLog(() =>
          tabtabOptions.reduce(
            (previousOperation, options) => previousOperation.then(() => uninstall(options)),
            BbPromise.resolve()
          )
        ).then(() =>
          this.serverless.cli.log('Tab autocompletion uninstalled (for all configured shells).')
        );
      });
    }
  }
  
  module.exports = Config;
  