List<String> ENVS = [
    'qa',
    'uat',
    'prod',
]
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 {
        choice(name: 'ENV', choices: ENVS, description: 'The environment to rollback.')
        string(name: 'SERVICE_NAME', defaultValue: '', trim: true, description: 'The name of the service to rollback, e.g. ows-product, graphql-knowledge')
        choice(name: 'AWS_REGION', choices: AWS_REGIONS, description: 'The AWS region of the service to rollback.')
        string(name: 'CLUSTER_NAME', defaultValue: '${ENV}-${SERVICE_NAME}', trim: true, description: 'The name of the ECS cluster.')
        string(name: 'FARGATE_SERVICE_NAME', defaultValue: '${ENV}-${SERVICE_NAME}', trim: true, description: 'The name of the Fargate service to rollback.')
        string(name: 'TASK_FAMILY', defaultValue: '${ENV}-${SERVICE_NAME}', trim: true, description: 'The name of the task family.')
        string(name: 'CONTAINER_NAME', defaultValue: '${SERVICE_NAME}', trim: true, description: 'The name of the container in the task definition.')
        choice(name: 'VERIFY_MODE', choices: ['HEALTH_CHECK', 'TASK_RUNNING'], description: 'The mode to verify the rollback. Select TASK_RUNNING if task does not support health checks')
        string(name: 'DEPLOY_MODE', defaultValue: 'REVERT', trim: true, description: 'The deployment mode. Use REVERT to rollback to previous task definition, or SPECIFIC to deploy a specific task definition revision.')
        string(name: 'REVERT_TASK_DEFINITION_NUMBER', defaultValue: '', trim: true, description: 'The task definition revision number to revert to. You can find this in the console output of the service\'s fargate deploy job')
        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.SERVICE_NAME?.trim() == '' || params.REVERT_TASK_DEFINITION_NUMBER?.trim() == '') {
                        error "SERVICE_NAME and REVERT_TASK_DEFINITION_NUMBER parameters are required."
                    }
                }
            }
        }
        stage('Set Build Description') {
            when {
                not { expression { params.REFRESH_PARAMETERS_ONLY } }
            }
            steps {
                script {
                    def accountId = AWS_ACCOUNT_IDS_BY_NAME[params.AWS_ACCOUNT]
                    currentBuild.description = "Fargate Rollback: arn:aws:ecs:${params.AWS_REGION}:${accountId}:service/${params.ENV}-${params.SERVICE_NAME} to revision ${params.REVERT_TASK_DEFINITION_NUMBER}"
                }
            }
        }
        stage('Rollback') {
            when {
                not { expression { params.REFRESH_PARAMETERS_ONLY } }
            }
            steps {
                script {
                    // Deploy using assumed role if not deploying to prod account
                    def role = DEPLOYMENT_ROLE_OVERRIDES.get(params.AWS_ACCOUNT, "${params.ENV}-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 fargate
                            python3 -m venv venv
                            . ./venv/bin/activate
                            pip install -r requirements.txt
                            python -u deploy.py
                        """
                    }
                }
            }
        }
    }

    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()
        }
    }
}
