text: 'use strict';
  
  const _ = require('lodash');
  const chalk = require('chalk');
  const { legacy, log } = require('@serverless/utils/log');
  const accountUtils = require('@serverless/utils/account');
  const configUtils = require('@serverless/utils/config');
  const { ServerlessSDK } = require('@serverless/platform-client');
  const promptWithHistory = require('@serverless/utils/inquirer/prompt-with-history');
  const { writeOrgAndApp, showOnboardingWelcome } = require('./utils');
  const {
    getPlatformClientWithAccessKey,
    getOrCreateAccessKeyForOrg,
  } = require('@serverless/dashboard-plugin/lib/clientUtils');
  
  const isValidAppName = RegExp.prototype.test.bind(/^[a-z0-9](?:[a-z0-9-]{0,126}[a-z0-9])?$/);
  
  const orgUpdateConfirm = async (stepHistory) => {
    legacy.write(
      "Service has monitoring enabled, but it is configured with the 'org' to which you do not have access\n\n"
    );
    log.notice(
      'Service has monitoring enabled, but it is configured with the "org" to which you do not have access'
    );
    log.notice();
    const shouldUpdateOrg = await promptWithHistory({
      message: 'Would you like to update it?',
      type: 'confirm',
      name: 'shouldUpdateOrg',
      stepHistory,
    });
    return shouldUpdateOrg;
  };
  
  const appUpdateConfirm = async (appName, orgName, stepHistory) => {
    legacy.write('The "app" used in this Service does not yet exist in your Organization.\n\n');
    log.notice('The "app" used in this Service does not yet exist in your Organization.');
    log.notice();
  
    const appUpdateType = await promptWithHistory({
      message: 'What would you like to do?',
      type: 'list',
      name: 'appUpdateType',
      stepHistory,
      choices: [
        { name: `Create '${appName}' app in '${orgName}' org`, value: '_create_' },
        {
          name: 'Switch to one of the existing apps (or create new one with different name)',
          value: '_choose_existing_',
        },
        { name: "Skip, I'll sort this out manually", value: '_skip_' },
      ],
    });
    return appUpdateType;
  };
  
  const orgsChoice = async (orgNames, stepHistory) => {
    const orgName = await promptWithHistory({
      message: 'What org do you want to add this service to?',
      type: 'list',
      name: 'orgName',
      choices: [...orgNames, { name: '[Skip]', value: '_skip_' }],
      stepHistory,
    });
    return orgName;
  };
  
  const appNameChoice = async (appNames, stepHistory) => {
    const appName = await promptWithHistory({
      message: 'What application do you want to add this to?',
      type: 'list',
      name: 'appName',
      choices: Array.from(appNames).concat({ name: '[create a new app]', value: '_create_' }),
      stepHistory,
    });
    return appName;
  };
  
  const appNameInput = async (appNames, stepHistory) => {
    const appName = await promptWithHistory({
      message: 'What do you want to name this application?',
      type: 'input',
      name: 'newAppName',
      stepHistory,
      validate: (input) => {
        input = input.trim();
        if (!isValidAppName(input)) {
          return (
            'App name is not valid.\n' +
            '   - It should only contain lowercase alphanumeric and hyphens.\n' +
            '   - It should start and end with an alphanumeric character.\n' +
            "   - Shouldn't exceed 128 characters"
          );
        }
        if (appNames.includes(input)) return 'App of this name already exists';
        return true;
      },
    });
    return appName;
  };
  
  const steps = {
    resolveOrgNames: async (user) => {
      if (process.env.SERVERLESS_ACCESS_KEY) {
        const sdk = new ServerlessSDK({ accessKey: process.env.SERVERLESS_ACCESS_KEY });
        const { orgName } = await sdk.accessKeys.get();
        return new Set([orgName]);
      }
  
      let orgs = new Set();
      if (!user.idToken) {
        // User registered over CLI hence idToken is not stored.
        // Still to resolve organizations from platform idToken is needed.
        // Handling it gently by assuming that orgs listed in config file
        // make a valid representation
        for (const org of Object.keys(user.accessKeys)) orgs.add(org);
      } else {
        const sdk = new ServerlessSDK();
        await accountUtils.refreshToken(sdk);
        user = configUtils.getLoggedInUser();
        sdk.config({ accessKey: user.idToken });
        orgs = new Set(
          (await sdk.organizations.list({ username: user.username })).map((org) => org.tenantName)
        );
      }
      return orgs;
    },
    setOrgAndApp: async (
      context,
      {
        orgNames,
        orgName,
        apps,
        appName,
        newAppName,
        isDashboardMonitoringOverridenByCli,
        isDashboardAppPreconfigured,
      }
    ) => {
      const { history, stepHistory } = context;
      if (!orgName) {
        orgName = await (async () => {
          // We only want to automatically select the single available org in situations where user explicitly
          // logged in/registered during the process and created new service, for existing services we want to always ask
          // that question. It will also be always asked if `SERVERLESS_ACCESS_KEY` was provided
          if (
            orgNames.size === 1 &&
            history &&
            history.has('dashboardLogin') &&
            history.has('service')
          ) {
            return orgNames.values().next().value;
          }
          return orgsChoice(orgNames, stepHistory);
        })();
      }
  
      if (orgName === '_skip_') {
        return;
      }
  
      const sdk = await getPlatformClientWithAccessKey(orgName);
      if (!newAppName && !appName) {
        if (!apps) apps = await sdk.apps.list({ orgName });
  
        const appNames = apps.map((app) => app.appName);
  
        if (!apps.length) {
          newAppName = context.configuration.service;
        } else {
          appName = await appNameChoice(appNames, stepHistory);
          if (appName === '_create_') {
            newAppName = await appNameInput(appNames, stepHistory);
          }
        }
      }
      if (newAppName) {
        ({ appName } = await sdk.apps.create({ orgName, app: { name: newAppName } }));
      }
      if (isDashboardMonitoringOverridenByCli && isDashboardAppPreconfigured) {
        const shouldOverrideDashboardConfig = await promptWithHistory({
          message:
            'Are you sure you want to update monitoring settings ' +
            `to ${chalk.bold(`app: ${appName}, org: ${orgName}`)}`,
          type: 'confirm',
          name: 'shouldOverrideDashboardConfig',
          stepHistory,
        });
        if (!shouldOverrideDashboardConfig) {
          delete context.configuration.app;
          delete context.configuration.org;
          return;
        }
      }
      legacy.write(
        `\n${chalk.green('Your project has been setup with org ')}${chalk.white.bold(
          orgName
        )}${chalk.green(' and app ')}${chalk.white.bold(appName)}\n`
      );
      log.notice();
      log.notice.success(`Your project has been setup with org "${orgName}" and app "${appName}"`);
      await writeOrgAndApp(orgName, appName, context);
      return;
    },
  };
  
  module.exports = {
    async isApplicable(context) {
      const { configuration, options, serviceDir } = context;
      if (!serviceDir) {
        context.inapplicabilityReasonCode = 'NOT_IN_SERVICE_DIRECTORY';
        return false;
      }
  
      if (
        _.get(configuration, 'provider') !== 'aws' &&
        _.get(configuration, 'provider.name') !== 'aws'
      ) {
        context.inapplicabilityReasonCode = 'NON_AWS_PROVIDER';
        return false;
      }
      const sdk = new ServerlessSDK();
      const { supportedRegions, supportedRuntimes } = await sdk.metadata.get();
      if (!supportedRuntimes.includes(_.get(configuration.provider, 'runtime') || 'nodejs12.x')) {
        context.inapplicabilityReasonCode = 'UNSUPPORTED_RUNTIME';
        return false;
      }
      if (
        !supportedRegions.includes(options.region || configuration.provider.region || 'us-east-1')
      ) {
        context.inapplicabilityReasonCode = 'UNSUPPORTED_REGION';
        return false;
      }
      const usesServerlessAccessKey = Boolean(process.env.SERVERLESS_ACCESS_KEY);
  
      let user = configUtils.getLoggedInUser();
      if (!user && !usesServerlessAccessKey) {
        context.inapplicabilityReasonCode = 'NOT_LOGGED_IN';
        return false;
      }
  
      const orgNames = await steps.resolveOrgNames(user);
      if (!orgNames.size) {
        context.inapplicabilityReasonCode = 'NO_ORGS_AVAILABLE';
        return false;
      }
      if (!usesServerlessAccessKey) {
        user = configUtils.getLoggedInUser(); // Refreshed, as new token might have been generated
      }
  
      const orgName = options.org || configuration.org;
      const appName = options.app || configuration.app;
  
      const isDashboardMonitoringPreconfigured = Boolean(configuration.org);
      const isDashboardAppPreconfigured = Boolean(configuration.app);
      const isDashboardMonitoringOverridenByCli =
        isDashboardMonitoringPreconfigured &&
        ((options.org && options.org !== configuration.org) ||
          (options.app && options.app !== configuration.app));
      if (orgName && orgNames.has(orgName)) {
        if (!isValidAppName(appName)) {
          return {
            user,
            orgName,
            isDashboardMonitoringPreconfigured,
            isDashboardAppPreconfigured,
            isDashboardMonitoringOverridenByCli,
          };
        }
  
        const accessKey = await getOrCreateAccessKeyForOrg(orgName);
        sdk.config({ accessKey });
        const apps = await sdk.apps.list({ orgName });
  
        if (options.org || options.app) {
          if (apps.some((app) => app.appName === appName)) {
            if (
              isDashboardMonitoringPreconfigured &&
              isDashboardAppPreconfigured &&
              !isDashboardMonitoringOverridenByCli
            ) {
              context.inapplicabilityReasonCode = 'MONITORING_NOT_OVERRIDEN_BY_CLI';
              return false;
            }
            return {
              user,
              orgName,
              appName,
              isDashboardMonitoringPreconfigured,
              isDashboardAppPreconfigured,
              isDashboardMonitoringOverridenByCli,
            };
          }
          if (options.app) {
            legacy.write(
              chalk.red(
                "\nPassed value for `--app` doesn't seem to correspond to chosen organization.\n"
              )
            );
            log.error();
            log.error('Passed value for "--app" doesn\'t seem to correspond to chosen organization.');
          }
          return {
            user,
            orgName,
            isDashboardMonitoringPreconfigured,
            isDashboardAppPreconfigured,
            isDashboardMonitoringOverridenByCli,
          };
        } else if (apps.some((app) => app.appName === appName)) {
          context.inapplicabilityReasonCode = 'HAS_MONITORING_SETUP';
          return false;
        }
        return {
          user,
          orgName,
          apps,
          newAppName: appName,
          isDashboardMonitoringPreconfigured,
          isDashboardAppPreconfigured,
          isDashboardMonitoringOverridenByCli,
        };
      } else if (orgName) {
        if (options.org) {
          legacy.write(
            chalk.red(
              "\nPassed value for `--org` doesn't seem to correspond to account with which you're logged in with.\n"
            )
          );
          log.error();
          log.error(
            'Passed value for "--org" doesn\'t seem to correspond to account with which you\'re logged in with.'
          );
        } else {
          legacy.write(
            chalk.red(`\nConfigured org '${orgName}' is not available in your account.\n`)
          );
          log.error();
          log.error(`Configured org "${orgName}" is not available in your account.`);
        }
      }
      return {
        user,
        orgNames,
        isDashboardMonitoringPreconfigured,
        isDashboardAppPreconfigured,
        isDashboardMonitoringOverridenByCli,
      };
    },
    async run(context, stepData) {
      const { configuration, options, stepHistory } = context;
      if (context.initial.isInServiceContext && !context.initial.isDashboardEnabled) {
        showOnboardingWelcome(context);
      }
  
      if (!stepData.orgName) delete configuration.org;
      if (!stepData.appName && !stepData.newAppName) delete configuration.app;
      if (!options.org && !options.app) {
        if (stepData.isDashboardMonitoringPreconfigured) {
          if (!stepData.orgName) {
            if (!(await orgUpdateConfirm(stepHistory))) return;
          } else if (stepData.newAppName) {
            const appUpdateTypeChoice = await appUpdateConfirm(
              stepData.newAppName,
              stepData.orgName,
              stepHistory
            );
            switch (appUpdateTypeChoice) {
              case '_create_':
                break;
              case '_choose_existing_':
                delete stepData.newAppName;
                break;
              case '_skip_':
                return;
              default:
                throw new Error('Unexpected app update type');
            }
          }
        }
      }
      await steps.setOrgAndApp(context, stepData);
    },
    steps,
    configuredQuestions: [
      'orgName',
      'appName',
      'newAppName',
      'shouldUpdateOrg',
      'appUpdateType',
      'shouldOverrideDashboardConfig',
    ],
  };
  