List<String> AWS_REGIONS = ['us-east-1']

String SHARED_ACCOUNT_ID = '086679231553'
String PROD_ACCOUNT_ID = '437795906767'
String BACKUP_ACCOUNT_ID = '079637511089'
String DEV_ACCOUNT_ID = '103233932089'
String QA_ACCOUNTING_ACCOUNT_ID = '989790945997'
String UAT_ACCOUNTING_ACCOUNT_ID = '989790945997'
String QA_SONGWHIP_ACCOUNT_ID = '619719722105'

String SHARED_DEPLOY_ROLE = 'shared-jenkins-pipeline-deploy-role'
String BACKUP_DEPLOY_ROLE = 'backup-jenkins-pipeline-deploy-role'
String PROD_DEPLOY_ROLE = 'prod-jenkins-aws-pipeline-agent'
String DEV_DEPLOY_ROLE = 'dev-jenkins-pipeline-access-role'
String QA_ACCOUNTING_DEPLOY_ROLE = 'qa-jenkins-pipeline-deploy-role'
String UAT_ACCOUNTING_DEPLOY_ROLE = 'uat-jenkins-pipeline-deploy-role'
String QA_SONGWHIP_DEPLOY_ROLE = 'qa-songwhip-jenkins-pipeline-deploy-role'

PROJECTS = [
    'fargate/create_shareable_snapshot': [
        additionalContexts: [
            common: 'common'
        ],
        deployType: 'CREATE_TASK_DEFINITION',
        imageName: 'rds-backup-create-shareable-snapshot',
        serviceType: 'FARGATE',
        services: [
            'rds-backup-create-shareable-snapshot': [
                environments: [
                    backup: [
                        awsAccount: BACKUP_ACCOUNT_ID,
                        awsDeploymentRole: BACKUP_DEPLOY_ROLE
                    ]
                ]
            ],
            'rds-refresh-create-shareable-snapshot': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ],
    ],
    'lambda/delete_intermediate_snapshots': [
        additionalContexts: [
            common: 'common'
        ],
        imageName: 'lambda-rds-backup-delete-intermediate-snapshots',
        serviceType: 'LAMBDA',
        services: [
            'lambda-rds-backup-delete-intermediate-snapshots': [
                environments: [
                    backup: [
                        awsAccount: BACKUP_ACCOUNT_ID,
                        awsDeploymentRole: BACKUP_DEPLOY_ROLE
                    ]
                ]
            ],
            'lambda-rds-refresh-delete-intermediate-snapshots': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ],
    ],
    'lambda/extract_native_configuration': [
        additionalContexts: [
            common: 'common'
        ],
        dockerBuildTarget: 'production',
        imageName: 'lambda-rds-backup-extract-native-configuration',
        serviceType: 'LAMBDA',
        services: [
            'lambda-rds-backup-extract-native-configuration': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ],
    'lambda/find_databases_to_back_up': [
        imageName: 'lambda-rds-backup-find-databases-to-back-up',
        serviceType: 'LAMBDA',
        services: [
            'lambda-rds-backup-find-databases-to-back-up': [
                environments: [
                    backup: [
                        awsAccount: BACKUP_ACCOUNT_ID,
                        awsDeploymentRole: BACKUP_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ],
    'lambda/get_db_info': [
        additionalContexts: [
            common: 'common'
        ],
        imageName: 'lambda-rds-utils-get-db-info',
        serviceType: 'LAMBDA',
        services: [
            'lambda-rds-refresh-get-db-info': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ],
    'lambda/manage_kafka_connector': [
        imageName: 'lambda-rds-utils-manage-kafka-connector',
        serviceType: 'LAMBDA',
        services: [
            'lambda-rds-refresh-manage-kafka-connector': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ],
    'lambda/sanitise_rds_data': [
        imageName: 'lambda-rds-utils-sanitise-rds-data',
        serviceType: 'LAMBDA',
        services: [
            'lambda-sanitise-rds-data': [
                environments: [
                    qa: [
                        awsAccount: PROD_ACCOUNT_ID,
                        awsDeploymentRole: PROD_DEPLOY_ROLE
                    ],
                    dev: [
                        awsAccount: DEV_ACCOUNT_ID,
                        awsDeploymentRole: DEV_DEPLOY_ROLE
                    ],
                    "qa-accounting": [
                        envTag: 'qa',
                        awsAccount: QA_ACCOUNTING_ACCOUNT_ID,
                        awsDeploymentRole: QA_ACCOUNTING_DEPLOY_ROLE
                    ],
                    "uat-accounting": [
                        envTag: 'uat',
                        awsAccount: UAT_ACCOUNTING_ACCOUNT_ID,
                        awsDeploymentRole: UAT_ACCOUNTING_DEPLOY_ROLE
                    ],
                    "qa-songwhip": [
                        envTag: 'qa',
                        awsAccount: QA_SONGWHIP_ACCOUNT_ID,
                        awsDeploymentRole: QA_SONGWHIP_DEPLOY_ROLE
                    ]
                ]
            ]
        ],
    ],
    'lambda/snowflake_refresh': [
        imageName: 'lambda-rds-utils-snowflake-refresh',
        serviceType: 'LAMBDA',
        services: [
            'lambda-rds-refresh-snowflake-refresh': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ],
    'lambda/fivetran_sync': [
        imageName: 'lambda-rds-utils-fivetran-sync',
        serviceType: 'LAMBDA',
        services: [
            'lambda-rds-refresh-fivetran-sync': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ],
    'fargate/manage_backup_snapshots': [
        additionalContexts: [
            common: 'common'
        ],
        deployType: 'CREATE_TASK_DEFINITION',
        imageName: 'rds-backup-manage-backup-snapshots',
        serviceType: 'FARGATE',
        services: [
            'rds-backup-manage-backup-snapshots': [
                environments: [
                    backup: [
                        awsAccount: BACKUP_ACCOUNT_ID,
                        awsDeploymentRole: BACKUP_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ],
    'fargate/restore': [
        additionalContexts: [
            common: 'common'
        ],
        deployType: 'CREATE_TASK_DEFINITION',
        imageName: 'rds-utils-restore',
        serviceType: 'FARGATE',
        services: [
            'rds-refresh-restore': [
                environments: [
                    shared: [
                        awsAccount: SHARED_ACCOUNT_ID,
                        awsDeploymentRole: SHARED_DEPLOY_ROLE
                    ]
                ]
            ]
        ]
    ]
]

pipeline {
    agent none

    options {
        timestamps()
        disableConcurrentBuilds()
    }

    parameters {
        string(name: 'PROJECTS', defaultValue: '', description: "Comma-separated list of projects to build. If not specified, all modified projects will be built.<br><p>Options:</p><ul><li>${PROJECTS.keySet().sort().join('</li><li>')}</li></ul>")
        string(name: 'SHARED_LIBRARIES_VERSION', defaultValue: 'master', description: 'The version of the Jenkins shared libraries to use. Can be a branch, tag or Git revision.')
    }

    triggers {
        issueCommentTrigger('.*retest this please.*')
    }

    stages {
        stage('Load Shared Libraries') {
            steps {
                library "jenkins-global-libraries@${params.SHARED_LIBRARIES_VERSION}"
            }
        }
        stage('Compliance Checks') {
            steps {
                withModifiedProjects(checkout: true) { project, projectConfig ->
                    dir(project) {
                        complianceChecks()
                    }
                }
            }
        }
        stage('Validate Software Catalog Definition - Common Module') {
            agent any
            when {
                expression { isCommonModuleChanged() }
            }
            steps {
                datadogSoftwareCatalogValidate(
                    servicePath: 'common',
                    serviceDefinitionFilePath: 'common/software-catalog.yaml'
                )
            }
        }
        stage('Validate Software Catalog Definitions') {
            steps {
                withModifiedProjects(checkout: true) { project, projectConfig ->
                    script {
                        parallel(projectConfig.get('services', []).collectEntries { service, serviceConfig ->
                            [
                                (service): {
                                    datadogSoftwareCatalogValidate(
                                        servicePath: project,
                                        serviceDefinitionFilePath: "${project}/software-catalog/${service}.yaml",
                                    )
                                }
                            ]
                        })
                    }
                }
            }
        }
        stage('Unit Tests and Style Checks - Common Module') {
            agent any
            when {
                expression { isCommonModuleChanged() }
            }
            steps {
                dir('common') {
                    withEcr {
                        sh 'make build_test'
                        sh 'make lint_and_test'
                    }
                }
            }
        }
        stage('Unit Tests and Style Checks') {
            steps {
                withModifiedProjects(checkout: true) { project, projectConfig ->
                    dir(project) {
                        withEcr {
                            sh 'make build_test'
                            sh 'make lint_and_test'
                        }
                    }
                }
            }
        }
        stage('Create a Release') {
            when {
                anyOf {
                    branch 'master'
                    expression { env.GITHUB_COMMENT =~ 'build docker' }
                }
            }
            steps {
                withModifiedProjects(checkout: true) { project, projectConfig ->
                    dockerToEcr awsRegions: AWS_REGIONS,
                        ecrAccountId: SHARED_ACCOUNT_ID,
                        imageName: projectConfig['imageName'],
                        imageTag: env.GIT_COMMIT,
                        dockerBuildContext: project,
                        dockerBuildFile: "${project}/Dockerfile",
                        dockerBuildTarget: projectConfig['dockerBuildTarget'],
                        additionalContexts: projectConfig['additionalContexts']
                }
            }
        }
        stage('Deploy') {
            when {
                branch 'master'
            }
            steps {
                withModifiedProjects { project, projectConfig ->
                    script {
                        parallel(projectConfig.get('services', []).collectEntries { service, serviceConfig ->
                            serviceConfig.get('environments', []).collectEntries { environment, envConfig ->
                                [
                                    "${environment}-${service}": {
                                        def envTag = envConfig.get('envTag', environment)
                                        if (projectConfig['serviceType'] == 'LAMBDA') {
                                            lambdaDeploy environment: envTag,
                                                functionName: "${envTag}-${service}",
                                                awsRegions: AWS_REGIONS,
                                                imageTag: env.GIT_COMMIT,
                                                imageName: projectConfig['imageName'],
                                                ecrRegistryAccountId: SHARED_ACCOUNT_ID,
                                                awsDeploymentTargetAccountId: envConfig['awsAccount'],
                                                awsDeploymentRoleName: envConfig['awsDeploymentRole']
                                        } else {
                                            fargateDeploy environment: envTag,
                                                awsRegions: AWS_REGIONS,
                                                gitCommit: env.GIT_COMMIT,
                                                serviceName: service,
                                                ecrRegistryAccountId: SHARED_ACCOUNT_ID,
                                                awsDeploymentTargetAccountId: envConfig['awsAccount'],
                                                awsDeploymentRoleName: envConfig['awsDeploymentRole'],
                                                deployType: projectConfig['deployType'],
                                                imageNameOverride: projectConfig['imageName']
                                        }
                                    }
                                ]
                            }
                        })
                    }
                }
            }
        }
        stage('Publish Software Catalog Definition - Common Module') {
            agent any
            when {
                allOf {
                    branch 'master'
                    expression { isCommonModuleChanged() }
                }
            }
            steps {
                datadogSoftwareCatalogPublish(
                    servicePath: 'common',
                    serviceDefinitionFilePath: 'common/software-catalog.yaml'
                )
            }
        }
        stage('Publish Software Catalog Definition') {
            when {
                branch 'master'
            }
            steps {
                withModifiedProjects(checkout: true) { project, projectConfig ->
                    script {
                        parallel(projectConfig.get('services', []).collectEntries { service, serviceConfig ->
                            [
                                (service): {
                                    datadogSoftwareCatalogPublish(
                                        servicePath: project,
                                        serviceDefinitionFilePath: "${project}/software-catalog/${service}.yaml",
                                    )
                                }
                            ]
                        })
                    }
                }
            }
        }
    }
}

def isCommonModuleChanged() {
    return getModifiedPaths(paths: ['common'])
}

def withModifiedProjects(Map args = [:], Closure steps) {
    getMonorepoUtils().withModifiedProjects(args, steps)
}

def getMonorepoUtils() {
    return library("jenkins-global-libraries@${params.SHARED_LIBRARIES_VERSION}").com.sonymusic.MonorepoUtils.getInstance(
        steps: this,
        projectConfig: PROJECTS,
        projectsToBuild: params.PROJECTS ? params.PROJECTS.split(',') : null,
        projectDependencies: [
            // Build projects that depend on common module if it has changed
            'common': PROJECTS.findAll { it.value.additionalContexts?.common }.keySet()
        ]
    )
}
