'use strict';

const Serverless = require('../../Serverless');
const chalk = require('chalk');
const { confirm, doesServiceInstanceHaveLinkedProvider } = require('./utils');
const _ = require('lodash');
const overrideStdoutWrite = require('process-utils/override-stdout-write');
const { getDashboardInteractUrl } = require('@serverless/dashboard-plugin/lib/dashboard');
const AWS = require('aws-sdk');
const isAuthenticated = require('@serverless/dashboard-plugin/lib/isAuthenticated');

const printMessage = ({
  serviceName,
  hasBeenDeployed,
  dashboardPlugin,
  isConfiguredWithDashboard,
}) => {
  if (isConfiguredWithDashboard) {
    if (hasBeenDeployed) {
      process.stdout.write(
        [
          `\n${chalk.green('Your project is live and available in ')}${chalk.white.bold(
            `./${serviceName}`
          )}`,
          `\n  Run ${chalk.bold('serverless info')} in the project directory`,
          '    View your endpoints and services',
          `\n  Open ${chalk.bold(getDashboardInteractUrl(dashboardPlugin))}`,
          '    Invoke your functions and view logs in the dashboard',
          `\n  Run ${chalk.bold('serverless deploy')} in the project directory`,
          "    Redeploy your service after you've updated your service code or configuration\n\n",
        ].join('\n')
      );

      return;
    }

    process.stdout.write(
      [
        `\n${chalk.green(
          'Your project is ready for deployment and available in '
        )}${chalk.white.bold(`./${serviceName}`)}`,
        `\n  Run ${chalk.bold('serverless deploy')} in the project directory`,
        '    Deploy your newly created service',
        `\n  Run ${chalk.bold('serverless info')} in the project directory after deployment`,
        '    View your endpoints and services',
        '\n  Open Serverless Dashboard after deployment',
        '    Invoke your functions and view logs in the dashboard\n\n',
      ].join('\n')
    );
    return;
  }

  if (hasBeenDeployed) {
    process.stdout.write(
      [
        `\n${chalk.green('Your project is live and available in ')}${chalk.white.bold(
          `./${serviceName}`
        )}`,
        `\n  Run ${chalk.bold('serverless info')} in the project directory`,
        '    View your endpoints and services',
        `\n  Run ${chalk.bold('serverless deploy')} in the directory`,
        "    Redeploy your service after you've updated your service code or configuration",
        `\n  Run ${chalk.bold('serverless invoke')} and ${chalk.bold(
          'serverless logs'
        )} in the project directory`,
        '    Invoke your functions directly and view the logs',
        `\n  Run ${chalk.bold('serverless')} in the project directory`,
        '    Add metrics, alerts, and a log explorer, by enabling the dashboard functionality\n\n',
      ].join('\n')
    );
    return;
  }

  process.stdout.write(
    [
      `\n${chalk.green('Your project is ready for deployment and available in ')}${chalk.white.bold(
        `./${serviceName}`
      )}`,
      `\n  Run ${chalk.bold('serverless deploy')} in the project directory`,
      '    Deploy your newly created service',
      `\n  Run ${chalk.bold('serverless info')} in the project directory after deployment`,
      '    View your endpoints and services',
      `\n  Run ${chalk.bold('serverless invoke')} and ${chalk.bold(
        'serverless logs'
      )} in the project directory after deployment`,
      '    Invoke your functions directly and view the logs',
      `\n  Run ${chalk.bold('serverless')} in the project directory`,
      '    Add metrics, alerts, and a log explorer, by enabling the dashboard functionality\n\n',
    ].join('\n')
  );
};

const configurePlugin = (serverless, originalStdWrite) => {
  serverless.pluginManager.addPlugin(require('./deploy-progress-plugin'));
  const interactivePlugin = serverless.pluginManager.plugins.find(
    (plugin) => plugin.constructor.name === 'InteractiveDeployProgress'
  );
  interactivePlugin.progress._writeOriginalStdout = (data) => originalStdWrite(data);
  return interactivePlugin;
};

module.exports = {
  async isApplicable(context) {
    const { configuration, serviceDir, history, options } = 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;
    }

    // If `awsCredentials` step was not executed, we should proceed as it means that user has available credentials
    if (!history.has('awsCredentials')) return true;

    // We want to proceed if the service instance has a linked provider
    if (
      configuration.org &&
      isAuthenticated() &&
      (await doesServiceInstanceHaveLinkedProvider({ configuration, options }))
    ) {
      return true;
    }

    // We want to proceed if local credentials are available
    if (new AWS.Config().credentials) return true;

    context.inapplicabilityReasonCode = 'NO_CREDENTIALS_CONFIGURED';
    return false;
  },
  async run({ configuration, configurationFilename, serviceDir, stepHistory }) {
    const serviceName = configuration.service;
    const shouldDeploy = await confirm('Do you want to deploy your project?', {
      name: 'shouldDeploy',
    });
    stepHistory.set('shouldDeploy', shouldDeploy);

    if (!shouldDeploy) {
      printMessage({
        serviceName,
        hasBeenDeployed: false,
        isConfiguredWithDashboard: Boolean(configuration.org),
      });
      return;
    }

    const serverless = new Serverless({
      configuration,
      serviceDir,
      configurationFilename,
      isConfigurationResolved: true,
      hasResolvedCommandsExternally: true,
      isTelemetryReportedExternally: true,
      commands: ['deploy'],
      options: {},
    });

    let interactiveOutputPlugin;

    try {
      await overrideStdoutWrite(
        () => {},
        async (originalStdWrite) => {
          // This is a hack to disable local fallback for interactive setup
          // After https://github.com/serverless/serverless/issues/1720 is addressed
          // We can change the approach to deployment in interactive setup and remove this hack
          serverless.isLocallyInstalled = true;
          await serverless.init();
          interactiveOutputPlugin = configurePlugin(serverless, originalStdWrite);
          // Remove previously set `isLocallyInstalled` as it was only needed to avoid local fallback in `init()`
          delete serverless.isLocallyInstalled;
          await serverless.run();
        }
      );
    } catch (err) {
      if (interactiveOutputPlugin) {
        interactiveOutputPlugin.handleError();
      }
      throw err;
    }

    printMessage({
      serviceName,
      hasBeenDeployed: true,
      isConfiguredWithDashboard: Boolean(configuration.org),
      dashboardPlugin: serverless.pluginManager.dashboardPlugin,
    });
  },
  configuredQuestions: ['shouldDeploy'],
};
