text: 'use strict';
  
  const BbPromise = require('bluebird');
  const getS3EndpointForRegion = require('../utils/getS3EndpointForRegion');
  
  const NO_UPDATE_MESSAGE = 'No updates are to be performed.';
  
  module.exports = {
    async createFallback() {
      this.createLater = false;
      this.serverless.cli.log('Creating Stack...');
  
      const stackName = this.provider.naming.getStackName();
      let stackTags = { STAGE: this.provider.getStage() };
      const compiledTemplateFileName = this.provider.naming.getCompiledTemplateS3Suffix();
      const s3Endpoint = getS3EndpointForRegion(this.provider.getRegion());
      const templateUrl = `https://${s3Endpoint}/${this.bucketName}/${this.serverless.service.package.artifactDirectoryName}/${compiledTemplateFileName}`;
  
      // Merge additional stack tags
      if (this.serverless.service.provider.stackTags) {
        const customKeys = Object.keys(this.serverless.service.provider.stackTags);
        const collisions = Object.keys(stackTags).filter((defaultKey) =>
          customKeys.some((key) => defaultKey.toLowerCase() === key.toLowerCase())
        );
  
        // Delete collisions upfront
        for (const key of collisions) {
          delete stackTags[key];
        }
  
        stackTags = Object.assign(stackTags, this.serverless.service.provider.stackTags);
      }
  
      const params = {
        StackName: stackName,
        OnFailure: 'DELETE',
        Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
        Parameters: [],
        TemplateURL: templateUrl,
        Tags: Object.keys(stackTags).map((key) => ({ Key: key, Value: stackTags[key] })),
      };
  
      if (
        this.serverless.service.provider.compiledCloudFormationTemplate &&
        this.serverless.service.provider.compiledCloudFormationTemplate.Transform
      ) {
        params.Capabilities.push('CAPABILITY_AUTO_EXPAND');
      }
  
      const customDeploymentRole = this.provider.getCustomDeploymentRole();
      if (customDeploymentRole) {
        params.RoleARN = customDeploymentRole;
      }
  
      if (this.serverless.service.provider.notificationArns) {
        params.NotificationARNs = this.serverless.service.provider.notificationArns;
      }
  
      if (this.serverless.service.provider.stackParameters) {
        params.Parameters = this.serverless.service.provider.stackParameters;
      }
  
      return this.provider
        .request('CloudFormation', 'createStack', params)
        .then((cfData) => this.monitorStack('create', cfData));
    },
  
    async update() {
      const compiledTemplateFileName = this.provider.naming.getCompiledTemplateS3Suffix();
      const s3Endpoint = getS3EndpointForRegion(this.provider.getRegion());
      const templateUrl = `https://${s3Endpoint}/${this.bucketName}/${this.serverless.service.package.artifactDirectoryName}/${compiledTemplateFileName}`;
  
      this.serverless.cli.log('Updating Stack...');
      const stackName = this.provider.naming.getStackName();
      let stackTags = { STAGE: this.provider.getStage() };
  
      // Merge additional stack tags
      if (this.serverless.service.provider.stackTags) {
        const customKeys = Object.keys(this.serverless.service.provider.stackTags);
        const collisions = Object.keys(stackTags).filter((defaultKey) =>
          customKeys.some((key) => defaultKey.toLowerCase() === key.toLowerCase())
        );
  
        // Delete collisions upfront
        for (const key of collisions) {
          delete stackTags[key];
        }
  
        stackTags = Object.assign(stackTags, this.serverless.service.provider.stackTags);
      }
  
      const params = {
        StackName: stackName,
        Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
        Parameters: [],
        TemplateURL: templateUrl,
        Tags: Object.keys(stackTags).map((key) => ({ Key: key, Value: stackTags[key] })),
      };
  
      if (
        this.serverless.service.provider.compiledCloudFormationTemplate &&
        this.serverless.service.provider.compiledCloudFormationTemplate.Transform
      ) {
        params.Capabilities.push('CAPABILITY_AUTO_EXPAND');
      }
  
      const customDeploymentRole = this.provider.getCustomDeploymentRole();
      if (customDeploymentRole) {
        params.RoleARN = customDeploymentRole;
      }
  
      if (this.serverless.service.provider.notificationArns) {
        params.NotificationARNs = this.serverless.service.provider.notificationArns;
      }
  
      if (this.serverless.service.provider.stackParameters) {
        params.Parameters = this.serverless.service.provider.stackParameters;
      }
  
      // Policy must have at least one statement, otherwise no updates would be possible at all
      if (
        this.serverless.service.provider.stackPolicy &&
        Object.keys(this.serverless.service.provider.stackPolicy).length
      ) {
        params.StackPolicyBody = JSON.stringify({
          Statement: this.serverless.service.provider.stackPolicy,
        });
      }
  
      if (this.serverless.service.provider.rollbackConfiguration) {
        params.RollbackConfiguration = this.serverless.service.provider.rollbackConfiguration;
      }
  
      return this.provider
        .request('CloudFormation', 'updateStack', params)
        .then((cfData) => this.monitorStack('update', cfData))
        .catch((e) => {
          if (e.message === NO_UPDATE_MESSAGE) {
            return BbPromise.resolve();
          }
          throw e;
        });
    },
  
    async updateStack() {
      return BbPromise.bind(this).then(() => {
        if (this.createLater) {
          return BbPromise.bind(this).then(this.createFallback);
        }
        return BbPromise.bind(this).then(this.update);
      });
    },
  };
  