List<String> AWS_REGIONS = [
    'us-east-1',
    'us-west-2',
    'eu-central-1',
]
def AWS_ACCOUNT_IDS_BY_NAME = getAwsAccountsMap()
def AWS_ACCOUNT_NAMES = AWS_ACCOUNT_IDS_BY_NAME.keySet() as List
AWS_ACCOUNT_NAMES = ['prod'] + (AWS_ACCOUNT_NAMES - ['prod']) // Ensure 'prod' is first in the list to make it the default choice

def DEPLOYMENT_ROLE_OVERRIDES = [
        'prod': 'prod-jenkins-aws-pipeline-agent',
        'dev': 'dev-jenkins-pipeline-access-role',
        'songwhip-qa': 'qa-songwhip-jenkins-pipeline-deploy-role',
        'songwhip-prod': 'prod-songwhip-jenkins-pipeline-deploy-role',
]

String SLACK_NOTIFICATIONS_CHANNEL = '#devops'

pipeline {
    agent {
        label 'aws'
    }

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

    parameters {
        string(name: 'FUNCTION_NAME', defaultValue: '', trim: true, description: 'Complete name of function, <strong>including</strong> environment prefix or suffix')
        string(name: 'FUNCTION_VERSION', defaultValue: '', trim: true, description: '(Optional) Specify a version for revert. If not provided, this job will revert to the last function version.')
        choice(name: 'AWS_REGION', choices: AWS_REGIONS, description: 'The AWS region of the service to rollback.')
        choice(name: 'AWS_ACCOUNT', choices: AWS_ACCOUNT_NAMES, description: 'The AWS account to deploy to.')
        string(name: 'SHARED_LIBRARIES_VERSION', defaultValue: 'master', description: 'The version of the Jenkins shared libraries to use. Can be a branch, tag, Git revision or PR ref (e.g. pull/PR_NUMBER/merge).')
        booleanParam(name: 'REFRESH_PARAMETERS_ONLY', defaultValue: false, description: 'Set this to just refresh the Jenkins job parameters without deploying anything.')
    }

    stages {
        stage('Load Shared Libraries') {
            steps {
                library "jenkins-global-libraries@${params.SHARED_LIBRARIES_VERSION}"
            }
        }
        stage('Validate Parameters') {
            when {
                not { expression { params.REFRESH_PARAMETERS_ONLY } }
            }
            steps {
                script {
                    if (params.FUNCTION_NAME?.trim() == '') {
                        error "FUNCTION_NAME parameter is required."
                    }
                    if (
                        !params.FUNCTION_NAME?.trim().startsWith('qa') &&
                        !params.FUNCTION_NAME?.trim().startsWith('uat') &&
                        !params.FUNCTION_NAME?.trim().startsWith('prod')
                    ) {
                        error "FUNCTION_NAME must start with the environment prefix (qa, uat, prod)."
                    }
                }
            }
        }
        stage('Set Build Description') {
            when {
                not { expression { params.REFRESH_PARAMETERS_ONLY } }
            }
            steps {
                script {

                    def accountId = AWS_ACCOUNT_IDS_BY_NAME[params.AWS_ACCOUNT]
                    def msg = ""
                    if (params.FUNCTION_VERSION?.trim() != '') {
                        msg = "to version ${params.FUNCTION_VERSION}"
                    }
                    currentBuild.description = "Lambda Rollback: arn:aws:lambda:${params.AWS_REGION}:${accountId}:function:${params.FUNCTION_NAME}" + " ${msg}"
                }
            }
        }
        stage('Rollback') {
            when {
                not { expression { params.REFRESH_PARAMETERS_ONLY } }
            }
            steps {
                script {
                    // Deploy using assumed role if not deploying to prod account
                    def envPrefix = params.FUNCTION_NAME.trim().split('-')[0]
                    def role = DEPLOYMENT_ROLE_OVERRIDES.get(params.AWS_ACCOUNT, "${envPrefix}-jenkins-pipeline-deploy-role")
                    def roleAccount = AWS_ACCOUNT_IDS_BY_NAME[params.AWS_ACCOUNT]
                    def roleSessionName = role

                    withAWS(role: role, roleAccount: roleAccount, roleSessionName: roleSessionName, useNode: true) {
                        sh '''
                            cd lambda
                            python3 -m venv venv
                            . ./venv/bin/activate
                            pip install -r requirements.txt
                            if [ -n "${FUNCTION_VERSION}" ]; then
                                echo "FUNCTION_VERSION specified"
                                python update_containerized_lambda.py -f "${FUNCTION_NAME}" -t dummy -i dummy -r -p -s "${FUNCTION_VERSION}"
                            else
                                echo "FUNCTION_VERSION not specified. Using last published function version"
                                python update_containerized_lambda.py -f "${FUNCTION_NAME}" -t dummy -i dummy -r -p
                            fi
                        '''
                    }
                }
            }
        }
    }

    post {
        regression {
            script {
                slackNotify channel: SLACK_NOTIFICATIONS_CHANNEL
            }
        }
        fixed {
            script {
                slackNotify channel: SLACK_NOTIFICATIONS_CHANNEL
            }
        }
        cleanup {
            cleanWs()
        }
    }
}

def getAwsAccountsMap() {
    node {
        withAWS(region: 'us-east-1', role: 'shared-jenkins-pipeline-deploy-role', roleAccount: '086679231553', roleSessionName: env.BUILD_TAG, useNode: true) {
            echo "Retrieving available AWS accounts..."
            // This parses json but only returns each list element as a string; so we then need to iterate through each string, parse, and filter it.
            def awsAccountMap = readJSON text: sh(returnStdout: true, script: "aws ssm get-parameters-by-path --path /shared/aws-account-ids/ --query 'Parameters[*].Value' --output json")
            def accountNameIdMap = [:]
            for (accountMapString in awsAccountMap) {
                def account = readJSON text: accountMapString
                accountNameIdMap[account['name']] = account['account_id']
            }
            return accountNameIdMap.sort()
        }
    }
}
