String GITHUB_REPOSITORY = 'bulk-shopify-onboarding'
String ECR_ACCOUNT_ID = '086679231553'
List<String> AWS_REGIONS = ['us-east-1']
String SLACK_NOTIFICATIONS_CHANNEL = '#d2c-engineering'

String QA_ACCOUNT_ID = '811473064144'
String QA_DEPLOYMENT_ROLE = 'qa-jenkins-pipeline-deploy-role'

String PROD_ACCOUNT_ID = '997286397029'
String PROD_DEPLOYMENT_ROLE = 'prod-jenkins-pipeline-deploy-role'

List<String> LAMBDA_FUNCTIONS = [
    'connect-shopify-stores',
    'onboard-shopify-stores',
    'sync-shopify-data-connectors',
]

List<String> VULNERABILITIES_TO_IGNORE = []

@groovy.transform.Field
List<String> functionsToBuild = null

pipeline {
    agent any

    options {
        ansiColor('xterm')
        disableConcurrentBuilds()
        timestamps()
    }

    parameters {
        booleanParam(name: 'DEPLOY_TO_QA', defaultValue: true, description: 'Whether or not to deploy to QA.')
        booleanParam(name: 'DEPLOY_TO_PROD', defaultValue: false, description: 'Whether or not to deploy to Prod.')
        string(name: 'LAMBDA_FUNCTION_NAME', defaultValue: '', description: 'Name of a single Lambda function directory to build and deploy (e.g. connect-shopify-stores). Leave blank to build all modified functions.')
        string(name: 'SHARED_LIBRARIES_VERSION', defaultValue: 'master', description: 'The version of the Jenkins shared libraries to use.')
    }

    triggers {
        issueCommentTrigger('.*(?:retest this please|build docker).*')
    }

    stages {
        stage('Load Shared Libraries') {
            steps {
                library "jenkins-global-libraries@${params.SHARED_LIBRARIES_VERSION}"
            }
        }

        stage('Unit Tests and Style Checks') {
            steps {
                script {
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Running unit tests for ${functionName}"
                            dir("lambda/${functionName}") {
                                sh 'docker compose run --rm --build lint-and-test'
                            }
                        }]
                    })
                }
            }
            post {
                always {
                    script {
                        getFunctionsToBuild().each { functionName ->
                            junit(allowEmptyResults: true,
                                  testResults: "lambda/${functionName}/build/pyunit.xml")
                        }
                    }
                }
            }
        }

        stage('Compliance Checks') {
            steps {
                script {
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Running Compliance Checks for ${functionName}"
                            dir("lambda/${functionName}") {
                                complianceChecks()
                            }
                        }]
                    })
                }
            }
        }

        stage('Validate Software Catalog Definitions') {
            steps {
                script {
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Validating Software Catalog definition for ${functionName}"
                            datadogSoftwareCatalogValidate(servicePath: "lambda/${functionName}")
                        }]
                    })
                }
            }
        }

        stage('Create a Release') {
            when {
                anyOf {
                    branch 'master'
                    expression { env.GITHUB_COMMENT =~ 'build docker' }
                }
            }
            steps {
                script {
                    def region = AWS_REGIONS[0]
                    sh """
                        aws ecr get-login-password --region ${region} | \
                        docker login --username AWS --password-stdin ${ECR_ACCOUNT_ID}.dkr.ecr.${region}.amazonaws.com
                    """
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Building image for ${functionName}"
                            dockerToEcr awsRegions: AWS_REGIONS,
                                ecrAccountId: ECR_ACCOUNT_ID,
                                imageName: "lambda-d2c-${functionName}",
                                imageTag: env.GIT_COMMIT,
                                dockerBuildContext: "lambda/${functionName}",
                                dockerBuildFile: "lambda/${functionName}/Dockerfile"
                        }]
                    })
                }
            }
        }

        stage('Scan Docker Images') {
            when {
                anyOf {
                    branch 'master'
                    expression { env.GITHUB_COMMENT =~ 'build docker' }
                }
            }
            steps {
                script {
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Scanning Docker image for ${functionName}"
                            dockerScan awsRegion: AWS_REGIONS[0],
                                ecrAccountId: ECR_ACCOUNT_ID,
                                imageName: "lambda-d2c-${functionName}",
                                imageTag: env.GIT_COMMIT,
                                vulnerabilitiesToIgnore: VULNERABILITIES_TO_IGNORE,
                                failBuild: false
                        }]
                    })
                }
            }
        }

        stage('Deploy to QA') {
            when {
                allOf {
                    expression { params.DEPLOY_TO_QA }
                    anyOf {
                        branch 'master'
                        expression { env.GITHUB_COMMENT =~ 'build docker' }
                    }
                }
            }
            steps {
                script {
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Deploying to QA: ${functionName}"
                            dir("lambda/${functionName}") {
                                lambdaDeploy environment: 'qa',
                                    awsRegions: AWS_REGIONS,
                                    imageTag: env.GIT_COMMIT,
                                    imageName: "lambda-d2c-${functionName}",
                                    ecrRegistryAccountId: ECR_ACCOUNT_ID,
                                    awsDeploymentTargetAccountId: QA_ACCOUNT_ID,
                                    awsDeploymentRoleName: QA_DEPLOYMENT_ROLE
                            }
                        }]
                    })
                }
            }
        }

        stage('Deploy to Prod') {
            when {
                allOf {
                    expression { params.DEPLOY_TO_PROD }
                    branch 'master'
                }
            }
            steps {
                script {
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Deploying to Prod: ${functionName}"
                            dir("lambda/${functionName}") {
                                lambdaDeploy environment: 'prod',
                                    awsRegions: AWS_REGIONS,
                                    imageTag: env.GIT_COMMIT,
                                    imageName: "lambda-d2c-${functionName}",
                                    ecrRegistryAccountId: ECR_ACCOUNT_ID,
                                    awsDeploymentTargetAccountId: PROD_ACCOUNT_ID,
                                    awsDeploymentRoleName: PROD_DEPLOYMENT_ROLE
                            }
                        }]
                    })
                }
            }
        }

        stage('Publish Software Catalog Definition') {
            when {
                branch 'master'
            }
            steps {
                script {
                    parallel(getFunctionsToBuild().collectEntries { functionName ->
                        [(functionName): {
                            echo "Updating Software Catalog definition for ${functionName}"
                            datadogSoftwareCatalogPublish(servicePath: "lambda/${functionName}")
                        }]
                    })
                }
            }
        }
    }

    post {
        regression {
            script {
                if (env.BRANCH_NAME == 'master') {
                    slackNotify channel: SLACK_NOTIFICATIONS_CHANNEL
                }
            }
        }
        fixed {
            script {
                if (env.BRANCH_NAME == 'master') {
                    slackNotify channel: SLACK_NOTIFICATIONS_CHANNEL
                }
            }
        }
        cleanup {
            cleanWs()
        }
    }
}

def getFunctionsToBuild() {
    if (this.@functionsToBuild != null) {
        return this.@functionsToBuild
    }

    def lambdaFunctionName = params.LAMBDA_FUNCTION_NAME ?: null

    if (lambdaFunctionName) {
        this.@functionsToBuild = [lambdaFunctionName]
    } else {
        def lambdaDirectories = findFiles(glob: '**/Dockerfile')
        this.@functionsToBuild = getModifiedFunctions branchName: env.BRANCH_NAME,
            previousSuccessfulCommit: env.GIT_PREVIOUS_SUCCESSFUL_COMMIT,
            lambdaDirectories: lambdaDirectories
    }

    if (this.@functionsToBuild) {
        currentBuild.description = this.@functionsToBuild.join('<br>')
    }

    return this.@functionsToBuild
}
