const ServerlessAWSCloudFormationSubVariables = require('serverless-cloudformation-sub-variables')

class ApiOrchestrator {
  constructor(serverless) {
    this.serverless = serverless

    // Register ${lambda:myFunctionKey} so that we can
    // refer to it from our Swagger/OpenAPI definition
    this.variableResolvers = {
      lambda: this.referenceLambda.bind(this),
    }

    // Somewhere to store our referenced lambdas across the lifecycle
    this.referencedKeys = []

    this.hooks = {

      // Prior to packaging we want to inject a fake HTTP event on the referenced function(s)
      // to piggyback on Serverless' logic for automatically creating API Gateway and their
      // deployments and allowing SLS to think that the method can be invoked by events.
      'after:package:setupProviderConfiguration': () => {

        // Lets loop through the lambdas we referenced earlier with our
        // ${lambda:something} syntax and add an HTTP event on them
        const functions = this.serverless.service.functions
        for (const functionKey of this.referencedKeys) {
          const func = functions[functionKey]
          const hasHttp = this.serverless.utils.isEventUsed([func], 'http')

          if (hasHttp) continue;
          functions[functionKey].events.push({ http: {
            path: 'sample/path',
            method: 'post',
          }})
        }
      },

      // After Serverless has generated the CloudFormation template we want to
      // clean it up from APIGateway-methods and -resources since the API definition
      // will create its own. Also clean up the artificial event.
      'aws:package:finalize:mergeCustomProviderResources': () => {
        const cfnTemplate = this.serverless.service.provider.compiledCloudFormationTemplate

        const cfnResourcesToBeRemoved = ['AWS::ApiGateway::Method', 'AWS::ApiGateway::Resource']
        for (const [key, resource] of Object.entries(cfnTemplate.Resources)) {
          // Filter unwanted resources
          if (cfnResourcesToBeRemoved.includes(resource.Type)) {
            delete cfnTemplate.Resources[key]
          }
          // Clean up refs to those resources
          if(resource.Type === 'AWS::ApiGateway::Deployment') {
            delete resource.DependsOn
          }
        }
        // Remove the artifical http event to avoid confusion in stored state
        // (manifests in e.g. post-deploy endpoint list)
        for (const functionKey of this.referencedKeys) {
          const func = this.serverless.service.functions[functionKey]
          func.events = func.events.filter((event) => {
            if (event.http) return false
            return true
          })
        }

        // Now before finishing up, lets replace all the fake Lambda references we added
        this.convertCfnVariables()
      },

      // Finally, if someone for some reason uses `serverless info`..
      'before:aws:info:displayEndpoints': () => {
        //TODO: For display purposes we should show the paths from Swagger
        const functions = this.serverless.service.functions
        for (const functionKey of this.referencedKeys) {
          functions[functionKey].events.push({ http: {
            path: '{?}',
            method: 'POTATO',
          }})
        }
      }
    }
  }

  async referenceLambda(src) {
    const functionKey = src.slice('lambda:'.length)
    const logicalId = this.serverless.getProvider('aws').naming.getLambdaLogicalId(functionKey)
    this.referencedKeys.push(functionKey)
    // this relies on another plugin to magically turn this string into
    // an actual reference in CloudFormation: serverless-cloudformation-sub-variables
    return `#{${logicalId}}`
  }

  convertCfnVariables() {
    const cfnVarConverter = new ServerlessAWSCloudFormationSubVariables(this.serverless)
    cfnVarConverter.convertSubVariables()
  }
}

module.exports = exports = ApiOrchestrator