'use strict';

const _ = require('lodash');
const crypto = require('crypto');

/**
 * A centralized naming object that provides naming standards for the AWS provider plugin.  The
 * intent is for this to enable naming standards to be configured using a custom naming plugin or
 * a naming configuration feature, based on which of those is determined to in the best approach.
 *
 * There are two main concepts:
 * 1. normalization - normalization of a name comprises removing any unacceptable characters and
 * changing the name to comply with capitalization expectations.
 * 2. logicalization - logicalization of a name comprises preparing it for use as an attribute name
 * for a resource within the Service's CloudFormation Template.  Notably, a suffix or prefix should
 * be added to the name of resources.  These help to disambiguate attributes identifying or
 * declaring different aspects or data about the same resource.  (e.g. MyFuncLambdaFunction vs.
 * MyFuncLambdaFunctionArn)  Generally, normalization of the name also occurs.
 *
 * Sometimes, it is important to deconstruct the names that are created via this centralized
 * utility.  As a result, the regular expressions used for identifying specific resource types using
 * their names or the logic to extract a name components from a logical name are things that must
 * also be centralized so that they co-occur and are more correlated spatially within the code base.
 * That is to say that they are easier to change together.
 */
module.exports = {
  // General
  normalizeName(name) {
    return `${_.upperFirst(name)}`;
  },
  normalizeNameToAlphaNumericOnly(name) {
    return this.normalizeName(name.replace(/[^0-9A-Za-z]/g, ''));
  },
  toStartCase(name) {
    return _.startCase(name).replace(/\s+/g, '');
  },
  normalizePathPart(rawPath) {
    return _.upperFirst(
      _.capitalize(rawPath)
        .replace(/-/g, 'Dash')
        .replace(/\{(.*)\}/g, '$1Var')
        .replace(/[^0-9A-Za-z]/g, '')
    );
  },

  getServiceEndpointRegex() {
    return /^(ServiceEndpoint|HttpApiUrl)/;
  },

  // Stack
  getStackName() {
    if (
      this.provider.serverless.service.provider.stackName &&
      typeof this.provider.serverless.service.provider.stackName === 'string'
    ) {
      return this.provider.serverless.service.provider.stackName;
    }
    return `${this.provider.serverless.service.service}-${this.provider.getStage()}`;
  },

  getStackChangeSetName() {
    return `${this.getStackName()}-change-set`;
  },

  getServiceArtifactName() {
    return `${this.provider.serverless.service.service}.zip`;
  },

  getFunctionArtifactName(functionName) {
    return `${functionName}.zip`;
  },

  getLayerArtifactName(layerName) {
    return `${layerName}.zip`;
  },

  getServiceStateFileName() {
    return 'serverless-state.json';
  },

  getCompiledTemplateFileName() {
    return 'cloudformation-template-update-stack.json';
  },

  getCompiledTemplateS3Suffix() {
    return 'compiled-cloudformation-template.json';
  },

  getCoreTemplateFileName() {
    return 'cloudformation-template-create-stack.json';
  },

  // Role
  getRolePath() {
    const customRolePath = _.get(this.provider, 'serverless.service.provider.iam.role.path');
    return customRolePath || '/';
  },
  getRoleName() {
    const customRoleName = _.get(this.provider, 'serverless.service.provider.iam.role.name');
    return (
      customRoleName || {
        'Fn::Join': [
          '-',
          [
            this.provider.serverless.service.service,
            this.provider.getStage(),
            { Ref: 'AWS::Region' },
            'lambdaRole',
          ],
        ],
      }
    );
  },
  getRoleLogicalId() {
    return 'IamRoleLambdaExecution';
  },

  // Policy
  getPolicyName() {
    return {
      'Fn::Join': [
        '-',
        [this.provider.serverless.service.service, this.provider.getStage(), 'lambda'],
      ],
    };
  },

  // Log Group
  getLogGroupLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LogGroup`;
  },
  getLogGroupName(functionName) {
    return `/aws/lambda/${functionName}`;
  },

  // Lambda
  getNormalizedFunctionName(functionName) {
    return this.getNormalizedResourceName(functionName);
  },
  extractLambdaNameFromArn(functionArn) {
    return functionArn.substring(functionArn.lastIndexOf(':') + 1);
  },
  extractAuthorizerNameFromArn(functionArn) {
    const splitArn = functionArn.split(':');
    // TODO the following two lines assumes default function naming?  Is there a better way?
    // TODO (see ~/lib/classes/service.js:~155)
    const splitName = splitArn[splitArn.length - 1].split('-');
    return splitName[splitName.length - 1];
  },
  getLambdaLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaFunction`;
  },
  getLambdaFunctionUrlLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaFunctionUrl`;
  },
  getLambdaEventConfigLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaEvConf`;
  },
  getLambdaLayerLogicalId(layerName) {
    return `${this.getNormalizedFunctionName(layerName)}LambdaLayer`;
  },
  getLambdaLayerPermissionLogicalId(layerName, account) {
    return `${this.getNormalizedFunctionName(layerName)}${account.replace(
      '*',
      'Wild'
    )}LambdaLayerPermission`;
  },
  getLambdaLogicalIdRegex() {
    return /LambdaFunction$/;
  },
  getLambdaVersionLogicalId(functionName, sha) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaVersion${sha.replace(
      /[^0-9a-z]/gi,
      ''
    )}`;
  },
  getCodeDeployApplicationLogicalId() {
    return 'CodeDeployApplication';
  },
  getCodeDeployDeploymentGroupLogicalId() {
    return 'CodeDeployDeploymentGroup';
  },
  getCodeDeployRoleLogicalId() {
    return 'CodeDeployRole';
  },
  getLambdaProvisionedConcurrencyAliasLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}ProvConcLambdaAlias`;
  },
  getLambdaSnapStartAliasLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}SnapStartLambdaAlias`;
  },
  getLambdaProvisionedConcurrencyAliasName() {
    return 'provisioned';
  },
  getLambdaSnapStartEnabledAliasName() {
    return 'snapstart';
  },
  getLambdaFunctionUrlOutputLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaFunctionUrl`;
  },
  getLambdaVersionOutputLogicalId(functionName) {
    return `${this.getLambdaLogicalId(functionName)}QualifiedArn`;
  },
  getLambdaLayerOutputLogicalId(layerName) {
    return `${this.getLambdaLayerLogicalId(layerName)}QualifiedArn`;
  },
  getLambdaLayerHashOutputLogicalId(layerName) {
    return `${this.getLambdaLayerLogicalId(layerName)}Hash`;
  },
  getLambdaLayerS3KeyOutputLogicalId(layerName) {
    return `${this.getLambdaLayerLogicalId(layerName)}S3Key`;
  },

  // Websockets API
  getWebsocketsApiName() {
    if (
      this.provider.serverless.service.provider.websocketsApiName &&
      typeof this.provider.serverless.service.provider.websocketsApiName === 'string'
    ) {
      return `${this.provider.serverless.service.provider.websocketsApiName}`;
    }
    return `${this.provider.getStage()}-${this.provider.serverless.service.service}-websockets`;
  },
  getWebsocketsApiLogicalId() {
    return 'WebsocketsApi';
  },
  getWebsocketsIntegrationLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}WebsocketsIntegration`;
  },

  getLambdaWebsocketsPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionWebsockets`;
  },

  getNormalizedWebsocketsRouteKey(route) {
    return route
      .replace(/\$/g, 'S') // dollar sign
      .replace(/\//g, 'Slash')
      .replace(/-/g, 'Dash')
      .replace(/_/g, 'Underscore')
      .replace(/\./g, 'Period');
  },

  getWebsocketsRouteLogicalId(route) {
    return `${this.getNormalizedWebsocketsRouteKey(route)}WebsocketsRoute`;
  },

  getWebsocketsDeploymentLogicalId(sha) {
    return `WebsocketsDeployment${sha.replace(/[^0-9a-z]/gi, '')}`;
  },

  getWebsocketsRouteResponseLogicalId(route) {
    return `${this.getWebsocketsRouteLogicalId(route)}Response`;
  },

  getWebsocketsStageLogicalId() {
    return 'WebsocketsDeploymentStage';
  },

  getWebsocketsAuthorizerLogicalId(functionName) {
    return `${this.getNormalizedAuthorizerName(functionName)}WebsocketsAuthorizer`;
  },
  getWebsocketsLogGroupLogicalId() {
    return 'WebsocketsLogGroup';
  },

  // API Gateway
  getApiGatewayName() {
    if (this.provider.serverless.service.provider.apiName) {
      return `${this.provider.serverless.service.provider.apiName}`;
    }

    return _.get(this.provider.serverless.service.provider.apiGateway, 'shouldStartNameWithService')
      ? `${this.provider.serverless.service.service}-${this.provider.getStage()}`
      : `${this.provider.getStage()}-${this.provider.serverless.service.service}`;
  },
  generateApiGatewayDeploymentLogicalId(id) {
    return `ApiGatewayDeployment${id}`;
  },
  getRestApiLogicalId() {
    return 'ApiGatewayRestApi';
  },
  getNormalizedAuthorizerName(functionName) {
    return this.getNormalizedFunctionName(functionName);
  },
  getAuthorizerLogicalId(functionName) {
    return `${this.getNormalizedAuthorizerName(functionName)}ApiGatewayAuthorizer`;
  },
  normalizePath(resourcePath) {
    return resourcePath.split('/').map(this.normalizePathPart.bind(this)).join('');
  },
  getResourceLogicalId(resourcePath) {
    return `ApiGatewayResource${this.normalizePath(resourcePath)}`;
  },
  extractResourceId(resourceLogicalId) {
    return resourceLogicalId.match(/ApiGatewayResource(.*)/)[1];
  },
  normalizeMethodName(methodName) {
    return this.normalizeName(methodName.toLowerCase());
  },
  getMethodLogicalId(resourceId, methodName) {
    return `ApiGatewayMethod${resourceId}${this.normalizeMethodName(methodName)}`;
  },
  getValidatorLogicalId() {
    return `ApiGateway${this.normalizeNameToAlphaNumericOnly(
      this.provider.serverless.service.service
    )}RequestValidator`;
  },
  getModelLogicalId(schemaId) {
    return `ApiGateway${_.upperFirst(_.camelCase(schemaId))}Model`;
  },
  getEndpointModelLogicalId(resourceId, methodName, contentType) {
    return `${this.getMethodLogicalId(resourceId, methodName)}${_.startCase(contentType).replace(
      / /g,
      ''
    )}Model`;
  },
  getApiKeyLogicalId(apiKeyNumber, apiKeyName) {
    if (apiKeyName) {
      return `ApiGatewayApiKey${this.normalizeName(apiKeyName)}${apiKeyNumber}`;
    }
    return `ApiGatewayApiKey${apiKeyNumber}`;
  },
  getApiKeyLogicalIdRegex() {
    return /^ApiGatewayApiKey/;
  },
  getUsagePlanLogicalId(name) {
    if (name) {
      return `ApiGatewayUsagePlan${this.normalizeName(name)}`;
    }
    return 'ApiGatewayUsagePlan';
  },
  getUsagePlanKeyLogicalId(usagePlanKeyNumber, usagePlanKeyName) {
    if (usagePlanKeyName) {
      return `ApiGatewayUsagePlanKey${this.normalizeName(usagePlanKeyName)}${usagePlanKeyNumber}`;
    }
    return `ApiGatewayUsagePlanKey${usagePlanKeyNumber}`;
  },
  getStageLogicalId() {
    return 'ApiGatewayStage';
  },
  getApiGatewayLogGroupLogicalId() {
    return 'ApiGatewayLogGroup';
  },

  // S3
  getDeploymentBucketLogicalId() {
    return 'ServerlessDeploymentBucket';
  },
  getDeploymentBucketOutputLogicalId() {
    return 'ServerlessDeploymentBucketName';
  },
  getDeploymentBucketPolicyLogicalId() {
    return 'ServerlessDeploymentBucketPolicy';
  },
  normalizeBucketName(bucketName) {
    return this.normalizeNameToAlphaNumericOnly(bucketName);
  },
  getBucketLogicalId(bucketName) {
    return `S3Bucket${this.normalizeBucketName(bucketName)}`;
  },

  // SNS
  normalizeTopicName(topicName) {
    return this.normalizeNameToAlphaNumericOnly(topicName);
  },
  getTopicLogicalId(topicName) {
    return `SNSTopic${this.normalizeTopicName(topicName)}`;
  },
  getTopicDLQPolicyLogicalId(functionName, topicName) {
    return `${this.normalizeTopicName(topicName)}To${this.getNormalizedFunctionName(
      functionName
    )}DLQPolicy`;
  },

  // Schedule
  getScheduleId(functionName) {
    return `${functionName}Schedule`;
  },
  getScheduleLogicalId(functionName, scheduleIndex) {
    return `${this.getNormalizedFunctionName(functionName)}EventsRuleSchedule${scheduleIndex}`;
  },
  getSchedulerScheduleLogicalId(functionName, scheduleIndex) {
    return `${this.getNormalizedFunctionName(functionName)}SchedulerSchedule${scheduleIndex}`;
  },

  // Stream
  getStreamLogicalId(functionName, streamType, streamName) {
    return `${this.getNormalizedFunctionName(functionName)}EventSourceMapping${this.normalizeName(
      streamType
    )}${this.normalizeNameToAlphaNumericOnly(streamName)}`;
  },
  getStreamConsumerName(functionName, streamName) {
    const consumerNamingMode = _.get(
      this.provider.serverless.service.provider.kinesis,
      'consumerNamingMode'
    );
    if (consumerNamingMode) {
      const serviceName = this.provider.serverless.service.getServiceName();
      const stage = this.provider.getStage();
      return `${functionName}${streamName}${serviceName}${stage}Consumer`;
    }

    return `${functionName}${streamName}Consumer`;
  },
  getStreamConsumerLogicalId(streamConsumerName) {
    return `${this.getNormalizedFunctionName(streamConsumerName)}StreamConsumer`;
  },

  // IoT
  getIotLogicalId(functionName, iotIndex) {
    return `${this.getNormalizedFunctionName(functionName)}IotTopicRule${iotIndex}`;
  },
  getLambdaIotPermissionLogicalId(functionName, iotIndex) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionIotTopicRule${iotIndex}`;
  },

  // IoT Fleet Provisioning
  getIotFleetProvisioningLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}IotProvisioningTemplate`;
  },
  getLambdaIotFleetProvisioningPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionIotProvisioningTemplate`;
  },

  // CloudWatch Event
  getCloudWatchEventId(functionName) {
    return `${functionName}CloudWatchEvent`;
  },
  getCloudWatchEventLogicalId(functionName, cloudWatchIndex) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}EventsRuleCloudWatchEvent${cloudWatchIndex}`;
  },

  // CloudWatch Log
  getCloudWatchLogLogicalId(functionName, logsIndex) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LogsSubscriptionFilterCloudWatchLog${logsIndex}`;
  },

  // Cognito User Pool
  getCognitoUserPoolLogicalId(poolId) {
    return `CognitoUserPool${this.normalizeNameToAlphaNumericOnly(poolId)}`;
  },

  // SQS
  getQueueLogicalId(functionName, queueName) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}EventSourceMappingSQS${this.normalizeNameToAlphaNumericOnly(queueName)}`;
  },

  // MSK
  getMSKEventLogicalId(functionName, clusterName, topicName) {
    const normalizedFunctionName = this.getNormalizedFunctionName(functionName);
    // Both clusterName and topicName are trimmed to 79 chars to avoid going over 255 character limit
    const normalizedClusterName = this.normalizeNameToAlphaNumericOnly(clusterName).slice(0, 79);
    const normalizedTopicName = this.normalizeNameToAlphaNumericOnly(topicName).slice(0, 79);
    return `${normalizedFunctionName}EventSourceMappingMSK${normalizedClusterName}${normalizedTopicName}`;
  },

  // Kafka
  getKafkaEventLogicalId(functionName, topicName) {
    const normalizedFunctionName = this.getNormalizedFunctionName(functionName);
    // TopicName is trimmed to 158 chars to avoid going over 255 character limit
    const normalizedTopicName = this.normalizeNameToAlphaNumericOnly(topicName).slice(0, 158);
    return `${normalizedFunctionName}EventSourceMappingKafka${normalizedTopicName}`;
  },

  // ActiveMQ
  getActiveMQEventLogicalId(functionName, queueName) {
    const normalizedFunctionName = this.getNormalizedFunctionName(functionName);
    // QueueName is trimmed to 150 chars to avoid going over 255 character limit
    const normalizedQueueName = this.normalizeNameToAlphaNumericOnly(queueName).slice(0, 150);
    return `${normalizedFunctionName}EventSourceMappingActiveMQ${normalizedQueueName}`;
  },

  // RabbitMQ
  getRabbitMQEventLogicalId(functionName, queueName) {
    const normalizedFunctionName = this.getNormalizedFunctionName(functionName);
    // QueueName is trimmed to 150 chars to avoid going over 255 character limit
    const normalizedQueueName = this.normalizeNameToAlphaNumericOnly(queueName).slice(0, 150);
    return `${normalizedFunctionName}EventSourceMappingRabbitMQ${normalizedQueueName}`;
  },

  // ALB
  getAlbTargetGroupLogicalId(functionName, albId, multiValueHeaders) {
    return `${this.getNormalizedFunctionName(functionName)}Alb${
      multiValueHeaders ? 'MultiValue' : ''
    }TargetGroup${albId}`;
  },
  getAlbTargetGroupNameTagValue(functionName, albId, multiValueHeaders) {
    return `${this.provider.serverless.service.service}-${functionName}-${albId}-${
      multiValueHeaders ? 'multi-value-' : ''
    }${this.provider.getStage()}`;
  },
  generateAlbTargetGroupName(functionName, albId, multiValueHeaders) {
    const hash = crypto
      .createHash('md5')
      .update(this.getAlbTargetGroupNameTagValue(functionName, albId, multiValueHeaders))
      .digest('hex');

    const albTargetGroupName = this.provider.getAlbTargetGroupPrefix() + hash;
    return albTargetGroupName.slice(0, 32);
  },
  getAlbListenerRuleLogicalId(functionName, idx) {
    return `${this.getNormalizedFunctionName(functionName)}AlbListenerRule${idx}`;
  },

  // Permissions
  getLambdaS3PermissionLogicalId(functionName, bucketName) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LambdaPermission${this.normalizeBucketName(bucketName)}S3`;
  },
  getLambdaSnsPermissionLogicalId(functionName, topicName) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LambdaPermission${this.normalizeTopicName(topicName)}SNS`;
  },
  getLambdaSnsSubscriptionLogicalId(functionName, topicName) {
    return `${this.getNormalizedFunctionName(functionName)}SnsSubscription${this.normalizeTopicName(
      topicName
    )}`;
  },
  getLambdaSchedulePermissionLogicalId(functionName, scheduleIndex) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LambdaPermissionEventsRuleSchedule${scheduleIndex}`;
  },
  getLambdaCloudWatchEventPermissionLogicalId(functionName, cloudWatchIndex) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LambdaPermissionEventsRuleCloudWatchEvent${cloudWatchIndex}`;
  },
  getLambdaApiGatewayPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionApiGateway`;
  },
  getLambdaHttpApiPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionHttpApi`;
  },
  getLambdaFnUrlPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionFnUrl`;
  },
  getLambdaAuthorizerHttpApiPermissionLogicalId(authorizerName) {
    return `${this.getNormalizedResourceName(authorizerName)}LambdaAuthorizerPermissionHttpApi`;
  },
  getLambdaAlexaSkillPermissionLogicalId(functionName, alexaSkillIndex) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlexaSkill${
      alexaSkillIndex || '0'
    }`;
  },
  getLambdaAlexaSmartHomePermissionLogicalId(functionName, alexaSmartHomeIndex) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LambdaPermissionAlexaSmartHome${alexaSmartHomeIndex}`;
  },
  getLambdaCloudWatchLogPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LambdaPermissionLogsSubscriptionFilterCloudWatchLog`;
  },
  getLambdaCognitoUserPoolPermissionLogicalId(functionName, poolId, triggerSource) {
    return `${this.getNormalizedFunctionName(
      functionName
    )}LambdaPermissionCognitoUserPool${this.normalizeNameToAlphaNumericOnly(
      poolId
    )}TriggerSource${this.normalizeNameToAlphaNumericOnly(triggerSource)}`;
  },
  getLambdaAlbPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlb`;
  },
  getLambdaRegisterTargetPermissionLogicalId(functionName) {
    return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionRegisterTarget`;
  },

  // Custom Resources
  getCustomResourcesArtifactName() {
    return 'custom-resources.zip';
  },
  getCustomResourcesRoleLogicalId() {
    return 'IamRoleCustomResourcesLambdaExecution';
  },
  // S3
  getCustomResourceS3HandlerFunctionName() {
    return 'custom-resource-existing-s3';
  },
  getCustomResourceS3HandlerFunctionLogicalId() {
    return this.getLambdaLogicalId(
      `${this.getNormalizedFunctionName(this.getCustomResourceS3HandlerFunctionName())}`
    );
  },
  getCustomResourceS3ResourceLogicalId(functionName) {
    // NOTE: we have to keep the 1 at the end to ensure backwards compatibility
    // previously we've used an index to allow the creation of multiple custom S3 resources
    // we're now using one resource to handle multiple S3 event definitions
    return `${this.getNormalizedFunctionName(functionName)}CustomS31`;
  },
  // Cognito User Pool
  getCustomResourceCognitoUserPoolHandlerFunctionName() {
    return 'custom-resource-existing-cup';
  },
  getCustomResourceCognitoUserPoolHandlerFunctionLogicalId() {
    return this.getLambdaLogicalId(
      `${this.getNormalizedFunctionName(
        this.getCustomResourceCognitoUserPoolHandlerFunctionName()
      )}`
    );
  },
  getCustomResourceCognitoUserPoolResourceLogicalId(functionName) {
    // NOTE: we have to keep the 1 at the end to ensure backwards compatibility
    // previously we've used an index to allow the creation of multiple custom
    // Cognito User Pool resources
    // we're now using one resource to handle multiple Cognito User Pool event definitions
    return `${this.getNormalizedFunctionName(functionName)}CustomCognitoUserPool1`;
  },
  // Event Bridge
  getCustomResourceEventBridgeHandlerFunctionName() {
    return 'custom-resource-event-bridge';
  },
  getCustomResourceEventBridgeHandlerFunctionLogicalId() {
    return this.getLambdaLogicalId(
      `${this.getNormalizedFunctionName(this.getCustomResourceEventBridgeHandlerFunctionName())}`
    );
  },
  getCustomResourceEventBridgeResourceLogicalId(functionName, idx) {
    return `${this.getNormalizedFunctionName(functionName)}CustomEventBridge${idx}`;
  },
  getNormalizedResourceName(resourceName) {
    return this.normalizeName(resourceName.replace(/-/g, 'Dash').replace(/_/g, 'Underscore'));
  },
  getEventBridgeEventBusLogicalId(eventBusName) {
    return `${this.getNormalizedResourceName(eventBusName)}EventBridgeEventBus`;
  },
  getEventBridgeRuleLogicalId(ruleName) {
    return `${this.normalizeNameToAlphaNumericOnly(ruleName)}EventBridgeRule`;
  },
  getEventBridgeLambdaPermissionLogicalId(functionName, idx) {
    return `${this.getNormalizedFunctionName(functionName)}EventBridgeLambdaPermission${idx}`;
  },

  // API Gateway Account Logs Write Role
  getCustomResourceApiGatewayAccountCloudWatchRoleHandlerFunctionName() {
    return 'custom-resource-apigw-cw-role';
  },
  getCustomResourceApiGatewayAccountCloudWatchRoleHandlerFunctionLogicalId() {
    return this.getLambdaLogicalId(
      `${this.getNormalizedFunctionName(
        this.getCustomResourceApiGatewayAccountCloudWatchRoleHandlerFunctionName()
      )}`
    );
  },
  getCustomResourceApiGatewayAccountCloudWatchRoleResourceLogicalId() {
    return 'CustomApiGatewayAccountCloudWatchRole';
  },

  // CloudFront
  getCloudFrontDistributionLogicalId() {
    return 'CloudFrontDistribution';
  },
  getCloudFrontDistributionDomainNameLogicalId() {
    return 'CloudFrontDistributionDomainName';
  },
  getLambdaAtEdgeInvokePermissionLogicalId(functionName) {
    return `${this.getLambdaLogicalId(functionName)}InvokePermission`;
  },
  getCloudFrontOriginId(originObj) {
    const isS3Origin = Boolean(originObj.S3OriginConfig);
    const domain = originObj.DomainName;
    const originPath = originObj.OriginPath;

    let originId = isS3Origin ? 's3' : 'custom';
    const domainName =
      typeof domain === 'string'
        ? domain
        : this.normalizeNameToAlphaNumericOnly(JSON.stringify(domain));

    originId = `${originId}/${domainName}`;
    if (originPath) {
      originId = `${originId}${originPath}`;
    }
    return originId;
  },
  getCloudFrontCachePolicyLogicalId(cachePolicyName) {
    return `CloudFrontCachePolicy${this.getNormalizedFunctionName(cachePolicyName)}`;
  },
  getCloudFrontCachePolicyName(cachePolicyName) {
    const serviceName = this.provider.serverless.service.getServiceName();
    const stage = this.provider.getStage();
    return `${serviceName}-${stage}-${cachePolicyName}`;
  },

  // HTTP API
  getHttpApiName() {
    if (
      this.provider.serverless.service.provider.httpApi &&
      this.provider.serverless.service.provider.httpApi.name
    ) {
      return `${String(this.provider.serverless.service.provider.httpApi.name)}`;
    }

    return _.get(this.provider.serverless.service.provider.httpApi, 'shouldStartNameWithService')
      ? `${this.provider.serverless.service.service}-${this.provider.getStage()}`
      : `${this.provider.getStage()}-${this.provider.serverless.service.service}`;
  },
  getHttpApiLogicalId() {
    return 'HttpApi';
  },
  getHttpApiStageLogicalId() {
    return 'HttpApiStage';
  },
  getHttpApiIntegrationLogicalId(functionName) {
    return `HttpApiIntegration${this.getNormalizedFunctionName(functionName)}`;
  },
  getHttpApiRouteLogicalId(routeKey) {
    if (routeKey === '*') return 'HttpApiRouteDefault';
    return `HttpApiRoute${this.normalizePath(routeKey)}`;
  },
  getHttpApiAuthorizerLogicalId(authorizerName) {
    return `HttpApiAuthorizer${this.getNormalizedFunctionName(authorizerName)}`;
  },
  getHttpApiLogGroupLogicalId() {
    return 'HttpApiLogGroup';
  },
  getHttpApiLogGroupName() {
    return `/aws/http-api/${this.getStackName()}`;
  },
  getEcrRepositoryName() {
    const serviceName = this.provider.serverless.service.getServiceName();
    const stage = this.provider.getStage();
    // Ensure no consecutive dashes are present and that there is to trailing slash left
    // TODO: Remove that with next major, assuming that issue #7056 (https://github.com/serverless/serverless/issues/7056) has been addressed
    return `serverless-${serviceName}-${stage}`.toLowerCase().replace(/-+/g, '-').replace(/-$/, '');
  },
};
