def MODE_PR = 'PR'
def MODE_PR_E2E = 'PR_E2E'
def MODE_OTA = 'OTA'
def MODE_NATIVE = 'NATIVE'
def MODE_INTERNAL_JS = 'INTERNAL_JS'

pipeline {
    agent {
        label 'linux'
    }

    parameters {
        booleanParam(name: 'SKIP_BUILD', defaultValue: false, description: 'Skip binary builds')
        booleanParam(name: 'DRY_RUN', defaultValue: true, description: 'Do not run jobs, just echo')
    }

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

    triggers {
        issueCommentTrigger('.*(retest this please|e2e|release|internal js).*')
    }

    environment {
        shared = null
        config = null
    }

    stages {
        stage('Detect Mode') {
            steps {
                script {
                    if (env.CHANGE_ID) {
                        def comment = env.GITHUB_COMMENT ?: ''
                        if (comment =~ /release/ && (env.CHANGE_BRANCH ?: '') =~ /^release\//) {
                            env.MODE = MODE_NATIVE
                        } else if (comment =~ /internal js/) {
                            env.MODE = MODE_INTERNAL_JS
                        } else if (comment =~ /e2e/) {
                            env.MODE = MODE_PR_E2E
                        } else {
                            env.MODE = MODE_PR
                        }
                    } else if (env.BRANCH_NAME =~ /^release\//) {
                        env.MODE = MODE_NATIVE
                    } else if (env.BRANCH_NAME == 'master') {
                        env.MODE = MODE_OTA
                    } else {
                        env.MODE = 'unknown'
                        echo "WARNING: No matching mode for branch '${env.BRANCH_NAME}'"
                    }
                    env.NOTIFY_SLACK = env.MODE in [MODE_OTA, MODE_NATIVE, MODE_INTERNAL_JS]
                    echo "env.MODE: ${env.MODE}"
                    echo "env.NOTIFY_SLACK: ${env.NOTIFY_SLACK}"
                    echo "env.BRANCH_NAME: ${env.BRANCH_NAME}"
                    echo "env.CHANGE_ID: ${env.CHANGE_ID}"
                    echo "env.CHANGE_BRANCH: ${env.CHANGE_BRANCH}"
                    echo "env.GITHUB_COMMENT: ${env.GITHUB_COMMENT}"
                    echo "env.GIT_COMMIT: ${env.GIT_COMMIT}"
                    echo "params.SKIP_BUILD: ${params.SKIP_BUILD}"
                }
            }
        }
        stage('Initialize') {
            steps {
                script {
                    shared = load './jenkins/shared.groovy'
                    config = load './jenkins/config.groovy'
                    library identifier: shared.library(), changelog: false
                    shared.initGitSha(env.GIT_COMMIT)
                    def effectiveDryRun = params.DRY_RUN
                    if (env.MODE == MODE_INTERNAL_JS && effectiveDryRun) {
                        echo "DRY_RUN override: forcing false because MODE=${env.MODE}"
                        effectiveDryRun = false
                    }
                    shared.initDryRun(effectiveDryRun)
                    if (env.NOTIFY_SLACK == 'true') {
                        shared.notifySlackStarted()
                    }
                }
            }
        }
        stage('PR') {
            when {
                expression {
                    env.MODE in [MODE_PR, MODE_PR_E2E]
                }
            }
            stages {
                stage('Unit tests') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobUnitTests(), [shared.gitCommit()])
                        }
                    }
                }
                stage('E2E') {
                    when {
                        expression {
                            env.MODE in [MODE_PR_E2E]
                        }
                    }
                    parallel {
                        stage('iOS') {
                            stages {
                                stage('iOS QA Build') {
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostIos(),
                                                shared.platformIos(),
                                                shared.brandOrchard(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                prAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('iOS E2E') {
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobE2E(), [
                                                shared.e2eBrandOrchard(),
                                                shared.e2eOsIos(),
                                                shared.e2eOsIosVer(),
                                                shared.e2eDeviceIos(),
                                                prE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                        stage('Android') {
                            stages {
                                stage('Android QA Build') {
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostAndroid(),
                                                shared.platformAndroid(),
                                                shared.brandOrchard(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                prAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('Android E2E') {
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobE2E(), [
                                                shared.e2eBrandOrchard(),
                                                shared.e2eOsAndroid(),
                                                shared.e2eOsAndroidVer(),
                                                shared.e2eDeviceAndroid(),
                                                prE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        stage('OTA') {
            when {
                expression {
                    env.MODE in [MODE_OTA, MODE_NATIVE]
                }
            }
            stages {
                stage('Unit tests') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobUnitTests(), [shared.gitCommit()])
                        }
                    }
                }
                stage('QA Bundle') {
                    steps {
                        script {
                            shared.runJob(shared.jobBundleQA(), [shared.gitCommit()])
                        }
                    }
                }
                stage('E2E') {
                    parallel {
                        stage('Orchard Android') {
                            stages {
                                stage('Orchard Android QA Build') {
                                    when {
                                        allOf {
                                            expression { return config.e2e.orchard }
                                            expression { return !params.SKIP_BUILD }
                                        }
                                    }
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostAndroid(),
                                                shared.platformAndroid(),
                                                shared.brandOrchard(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                mobilePipelineAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('Orchard Android E2E') {
                                    when {
                                        expression { return config.e2e.orchard }
                                    }
                                    steps {
                                        script {
                                            shared.runJob(shared.jobE2E(), [
                                                shared.e2eBrandOrchard(),
                                                shared.e2eOsAndroid(),
                                                shared.e2eOsAndroidVer(),
                                                shared.e2eDeviceAndroid(),
                                                mobilePipelineE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                        stage('Awal Android') {
                            stages {
                                stage('Awal Android QA Build') {
                                    when {
                                        allOf {
                                            expression { return config.e2e.awal }
                                            expression { return !params.SKIP_BUILD }
                                        }
                                    }
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostAndroid(),
                                                shared.platformAndroid(),
                                                shared.brandAwal(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                mobilePipelineAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('Awal Android E2E') {
                                    when {
                                        expression { return config.e2e.awal }
                                    }
                                    steps {
                                        script {
                                            shared.runJob(shared.jobE2E(), [
                                                shared.e2eBrandAwal(),
                                                shared.e2eOsAndroid(),
                                                shared.e2eOsAndroidVer(),
                                                shared.e2eDeviceAndroid(),
                                                mobilePipelineE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                        stage('SME Android') {
                            stages {
                                stage('SME Android QA Build') {
                                    when {
                                        allOf {
                                            expression { return config.e2e.sme }
                                            expression { return !params.SKIP_BUILD }
                                        }
                                    }
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostAndroid(),
                                                shared.platformAndroid(),
                                                shared.brandSme(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                mobilePipelineAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('SME Android E2E') {
                                    when {
                                        expression { return config.e2e.sme }
                                    }
                                    steps {
                                        script {
                                            shared.runJob(shared.jobE2E(), [
                                                shared.e2eBrandSme(),
                                                shared.e2eOsAndroid(),
                                                shared.e2eOsAndroidVer(),
                                                shared.e2eDeviceAndroid(),
                                                mobilePipelineE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                        stage('Orchard iOS') {
                            stages {
                                stage('Orchard iOS QA Build') {
                                    when {
                                        allOf {
                                            expression { return config.e2e.orchard }
                                            expression { return !params.SKIP_BUILD }
                                        }
                                    }
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostIos(),
                                                shared.platformIos(),
                                                shared.brandOrchard(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                mobilePipelineAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('Orchard iOS E2E') {
                                    when {
                                        expression { return config.e2e.orchard }
                                    }
                                    steps {
                                        script {
                                            shared.runJob(shared.jobE2E(), [
                                                shared.e2eBrandOrchard(),
                                                shared.e2eOsIos(),
                                                shared.e2eOsIosVer(),
                                                shared.e2eDeviceIos(),
                                                mobilePipelineE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                        stage('Awal iOS') {
                            stages {
                                stage('Awal iOS QA Build') {
                                    when {
                                        allOf {
                                            expression { return config.e2e.awal }
                                            expression { return !params.SKIP_BUILD }
                                        }
                                    }
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostIos(),
                                                shared.platformIos(),
                                                shared.brandAwal(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                mobilePipelineAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('Awal iOS E2E') {
                                    when {
                                        expression { return config.e2e.awal }
                                    }
                                    steps {
                                        script {
                                            shared.runJob(shared.jobE2E(), [
                                                shared.e2eBrandAwal(),
                                                shared.e2eOsIos(),
                                                shared.e2eOsIosVer(),
                                                shared.e2eDeviceIos(),
                                                mobilePipelineE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                        stage('SME iOS') {
                            stages {
                                stage('SME iOS QA Build') {
                                    when {
                                        allOf {
                                            expression { return config.e2e.sme }
                                            expression { return !params.SKIP_BUILD }
                                        }
                                    }
                                    steps {
                                        script {
                                            shared.runJobWithRetry(shared.jobBinary(), [
                                                shared.gitCommit(),
                                                hostIos(),
                                                shared.platformIos(),
                                                shared.brandSme(),
                                                shared.environmentQa(),
                                                shared.storeS3(),
                                                shared.androidBinaryTypeApk(),
                                                mobilePipelineAwsBinariesNamePrefix()
                                            ])
                                        }
                                    }
                                }
                                stage('SME iOS E2E') {
                                    when {
                                        expression { return config.e2e.sme }
                                    }
                                    steps {
                                        script {
                                            shared.runJob(shared.jobE2E(), [
                                                shared.e2eBrandSme(),
                                                shared.e2eOsIos(),
                                                shared.e2eOsIosVer(),
                                                shared.e2eDeviceIos(),
                                                mobilePipelineE2EPrefix()
                                            ])
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                stage('Prod Bundle') {
                    when {
                        expression { return config.prodCodePushEnabled }
                    }
                    steps {
                        script {
                            shared.runJob(shared.jobBundleProd(), [shared.gitCommit()])
                        }
                    }
                }
            }
        }
        stage('Internal Testing Bundle') {
            when {
                expression {
                    env.MODE in [MODE_INTERNAL_JS]
                }
            }
            steps {
                script {
                    shared.runJob(shared.jobBundleQA(), [
                        shared.gitCommit(),
                        shared.internalTesting(true)
                    ])
                }
            }
        }
        stage('Native') {
            when {
                expression {
                    env.MODE in [MODE_NATIVE]
                }
            }
            parallel {
                stage('Orchard Android Build') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobBinary(), [
                                shared.gitCommit(),
                                hostAndroid(),
                                shared.platformAndroid(),
                                shared.brandOrchard(),
                                shared.environmentProd(),
                                shared.storeStore(),
                                shared.androidBinaryTypeApk()
                            ])
                        }
                    }
                }
                stage('Awal Android Build') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobBinary(), [
                                shared.gitCommit(),
                                hostAndroid(),
                                shared.platformAndroid(),
                                shared.brandAwal(),
                                shared.environmentProd(),
                                shared.storeStore(),
                                shared.androidBinaryTypeApk()
                            ])
                        }
                    }
                }
                stage('SME Android Build') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobBinary(), [
                                shared.gitCommit(),
                                hostAndroid(),
                                shared.platformAndroid(),
                                shared.brandSme(),
                                shared.environmentProd(),
                                shared.storeStore(),
                                shared.androidBinaryTypeAab()
                            ])
                        }
                    }
                }
                stage('Orchard iOS Build') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobBinary(), [
                                shared.gitCommit(),
                                hostIos(),
                                shared.platformIos(),
                                shared.brandOrchard(),
                                shared.environmentProd(),
                                shared.storeStore(),
                                shared.androidBinaryTypeApk()
                            ])
                        }
                    }
                }
                stage('Awal iOS Build') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobBinary(), [
                                shared.gitCommit(),
                                hostIos(),
                                shared.platformIos(),
                                shared.brandAwal(),
                                shared.environmentProd(),
                                shared.storeStore(),
                                shared.androidBinaryTypeApk()
                            ])
                        }
                    }
                }
                stage('SME iOS Build') {
                    steps {
                        script {
                            shared.runJobWithRetry(shared.jobBinary(), [
                                shared.gitCommit(),
                                hostIos(),
                                shared.platformIos(),
                                shared.brandSme(),
                                shared.environmentProd(),
                                shared.storeStore(),
                                shared.androidBinaryTypeApk()
                            ])
                        }
                    }
                }
            }
        }
    }
    post {
        always {
            script {
                echo "Finished. MODE: ${env.MODE}, Result: ${currentBuild.result}"
                if (env.CHANGE_ID) { cleanWs() }
            }
        }
        success {
            script {
                if (env.NOTIFY_SLACK == 'true') {
                    shared.notifySlackFinished()
                }
            }
        }
        failure {
            script {
                if (env.NOTIFY_SLACK == 'true') {
                    shared.notifySlackFinished(true)
                }
            }
        }
        aborted {
            script {
                if (env.NOTIFY_SLACK == 'true') {
                    shared.notifySlackFinished(true)
                }
            }
        }
        fixed {
            script {
                if (env.NOTIFY_SLACK == 'true') {
                    shared.notifySlackFinished(true)
                }
            }
        }
    }
}

def mobilePipelineAwsBinariesNamePrefix() {
    return shared.awsBinariesNamePrefix(config.mobilePipeline.binariesPrefix)
}

def mobilePipelineE2EPrefix() {
    return shared.e2ePrefix(config.mobilePipeline.binariesPrefix)
}

def prAwsBinariesNamePrefix() {
    return shared.awsBinariesNamePrefix('prE2E')
}

def prE2EPrefix() {
    return shared.e2ePrefix('prE2E')
}

def hostAndroid() {
    return shared.host(config.host.android)
}

def hostIos() {
    return shared.host(config.host.ios)
}
