def call() {
  def BRANCHES_TO_DEPLOY = [
    'master', 'develop', 'develop2'
  ]

  pipeline {

    agent {
      label 'linux-agents-2xlarge'
    }

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

    parameters {
      choice(name: 'RELEASE_CHANNEL',
              choices: ['Please make a choice', 'qa', 'qa2', 'dev', 'dev2', 'staging', 'test', 'uat', 'preprod', 'production'],
              description: 'Expo channel')
      extendedChoice(defaultValue: 'false',
              description: 'Force builds',
              multiSelectDelimiter: ',',
              name: 'FORCE',
              quoteValue: false,
              saveJSONParameterToFile: false,
              type: 'PT_CHECKBOX',
              value: 'ANDROID_BUILD_APK,ANDROID_BUILD_AAB,ANDROID_DEBUG_BUILD_APK,IOS_BUILD,IOS_SIMULATOR_BUILD,IOS_AD_HOC,IOS_DEBUG_AD_HOC',
              visibleItemCount: 7)
      booleanParam(defaultValue: false,
              description: 'Skip typescript linter',
              name: 'SKIP_TS_LINT')
      booleanParam(defaultValue: false,
              description: 'Skip style linter',
              name: 'SKIP_STYLE_LINT')
      booleanParam(defaultValue: false,
              description: 'Skip linter',
              name: 'SKIP_LINTER')
      booleanParam(defaultValue: false,
              description: 'Skip unit tests',
              name: 'SKIP_TESTS')
      booleanParam(defaultValue: false,
              description: 'Skip docker image cache',
              name: 'NO_CACHE')
    }

    environment {
      AWS_PROFILE        = 'gdb-delphi-dev'
      EXPO_TOKEN         = utility.getSecretValueById('infra/jenkins/apollo/common/apollogo/EXPO_CLI_TOKEN', env.AWS_PROFILE)
      LOCAL_BUILD_TAG    = env.BUILD_TAG.replace('/', '-').replace('%2F', '-')
      LOCAL_BRANCH_NAME  = env.BRANCH_NAME.replace('/', '-').replace('%2F', '-')
      IMAGE_TAG          = "${env.LOCAL_BUILD_TAG}" + "-" + "${env.GIT_COMMIT}"
      VERSION            = sh(script: 'jq -r .version package.json', returnStdout: true).trim()
      LOCAL_BUILD_NUMBER = "${env.VERSION}-${env.BUILD_NUMBER}"
      RELEASE_CHANNEL    = "${(!params.RELEASE_CHANNEL.equals('Please make a choice')) ? params.RELEASE_CHANNEL : apolloGoHelpers.getReleaseChannelByBranchName(env.BRANCH_NAME)}"
      ECR                = '725764004555.dkr.ecr.us-east-1.amazonaws.com'

      SENTRY_AUTH_TOKEN = utility.getSecretValueById('infra/dev/atlantis/common/SENTRY_API_KEY', 'gdb-infra-dev')
      SENTRY_DSN        = utility.getSecretValueById('infra/jenkins/apollo/apollogo/SENTRY_DSN', 'gdb-infra-dev')
      SENTRY_URL        = 'https://the-orchard.sentry.io'

      AUTH0_DOMAIN_DEV        = utility.getSecretValueById('infra/jenkins/apollo/dev/apollogo/AUTH0_DOMAIN', env.AWS_PROFILE)
      AUTH0_CLIENT_ID_DEV     = utility.getSecretValueById('infra/jenkins/apollo/dev/apollogo/AUTH0_CLIENT_ID', env.AWS_PROFILE)
      AUTH0_CLIENT_SECRET_DEV = utility.getSecretValueById('infra/jenkins/apollo/dev/apollogo/AUTH0_CLIENT_SECRET', env.AWS_PROFILE)
      AUTH0_AUDIENCE_DEV      = utility.getSecretValueById('infra/jenkins/apollo/dev/apollogo/AUTH0_AUDIENCE', env.AWS_PROFILE)

      AUTH0_DOMAIN_PROD        = utility.getSecretValueById('infra/jenkins/apollo/prod/apollogo/AUTH0_DOMAIN', env.AWS_PROFILE)
      AUTH0_CLIENT_ID_PROD     = utility.getSecretValueById('infra/jenkins/apollo/prod/apollogo/AUTH0_CLIENT_ID', env.AWS_PROFILE)
      AUTH0_CLIENT_SECRET_PROD = utility.getSecretValueById('infra/jenkins/apollo/prod/apollogo/AUTH0_CLIENT_SECRET', env.AWS_PROFILE)
      AUTH0_AUDIENCE_PROD      = utility.getSecretValueById('infra/jenkins/apollo/prod/apollogo/AUTH0_AUDIENCE', env.AWS_PROFILE)

      AMPLITUDE_KEY_DEV  = utility.getSecretValueById('infra/jenkins/apollo/dev/apollogo/AMPLITUDE_KEY', env.AWS_PROFILE)
      AMPLITUDE_KEY_PROD = utility.getSecretValueById('infra/jenkins/apollo/prod/apollogo/AMPLITUDE_KEY', env.AWS_PROFILE)

      SPLIT_IO_KEY_DEV  = utility.getSecretValueById('infra/jenkins/apollo/dev/apollogo/SPLIT_IO_KEY', env.AWS_PROFILE)
      SPLIT_IO_KEY_PROD = utility.getSecretValueById('infra/jenkins/apollo/prod/apollogo/SPLIT_IO_KEY', env.AWS_PROFILE)

      NOTIFICATIONS_CHANNEL = "${env.CHANGE_ID && params.FORCE.equals('Please make a choice') ? '#apollo-go-ci-alerts' : '#apollo-go-deployments'}"
    }

    stages {
      stage('Validate Input Parameters') {
        when {
          allOf {
            expression { params.RELEASE_CHANNEL.equals('production') }
            not { expression { env.BRANCH_NAME.equals('master') } }
          } /** end: allOf */
        } /** end: when */

        steps {
          error 'Invalid input parameters, you can only deploy to production release channel from master branch'
        } /** end: steps */
      } /** end: stage */

      stage('Install Dependencies') {
        options {
          retry(3)
        }

        steps {
          script {
            currentBuild.displayName = env.LOCAL_BUILD_NUMBER
            currentBuild.description = ''
            apolloGoHelpers.installDependencies()
          }
        } /** end: steps */
      } /** end: stage */

      stage('Cache docker image') {
        options {
          retry(3)
        }

        when {
          branch comparator: 'EQUALS', pattern: 'develop'
        }

        steps {
          script {
            apolloGoHelpers.publishDockerImage()
          }
        }
      }

      stage('TypeScript lint') {
        when {
          not { expression { params.SKIP_TS_LINT } }
        }

        steps {
          sh "docker run apollo-go:${env.IMAGE_TAG} yarn run tslint"
        } /** end: steps */
      } /** end: stage */

      stage('Style lint') {
        when {
          not { expression { params.SKIP_STYLE_LINT } }
        }

        steps {
          sh "docker run apollo-go:${env.IMAGE_TAG} yarn run stylelint"
        } /** end: steps */
      } /** end: stage */

      stage('Linter') {
        when {
          not { expression { params.SKIP_LINTER } }
        }

        steps {
          sh "docker run apollo-go:${env.IMAGE_TAG} yarn run lint"
        } /** end: steps */
      } /** end: stage */

      stage('Test') {
        when {
          not { expression { params.SKIP_TESTS } }
        }

        environment {
          JEST_JUNIT_OUTPUT_DIR  = 'output'
          JEST_JUNIT_OUTPUT_NAME = 'junit.xml'
        }

        steps {
          script {
            apolloGoHelpers.runTests()
          }
        } /** end: steps */

        post {
          always {
            sh "sudo chown -R \$(whoami):\$(whoami) ${env.JEST_JUNIT_OUTPUT_DIR}"
            junit allowEmptyResults: true, testResults: "${env.JEST_JUNIT_OUTPUT_DIR}/${env.JEST_JUNIT_OUTPUT_NAME}"
            script {  testSummary = utility.getTestSummary() }
            sh 'ls -ashil output/coverage/jest/'
            recordCoverage(
              tools: [[
                parser: 'COBERTURA',
                pattern: '**/output/coverage/jest/cobertura-coverage.xml'
              ]],
              healthy: [lineCoverage: 80, methodCoverage: 80, conditionalCoverage: 70],
              unhealthy: [lineCoverage: 0, methodCoverage: 0, conditionalCoverage: 0],
              failing: [lineCoverage: 0, methodCoverage: 0, conditionalCoverage: 0],
              sourceDirectories: [[path: '.']],
              enabledForFailure: true,
              failOnError: false
            )
          } /** end: always */
        } /** end: post */
      } /** end: stage */

      stage('Swagger') {
        when {
          allOf {
            branch 'develop'
            triggeredBy cause: 'BranchEventCause'
          }
        }

        environment {
          AWS_PROFILE = 'gdb-apollo-dev'
        }

        steps {
          script {
            apolloGoHelpers.fixPermissions()
            apolloGoHelpers.generateAndSyncSwagger()
          }
        } /** end: steps */

        post {
          always {
            script { apolloGoHelpers.fixPermissions() }
          }
        } /** end: post */
      } /** end: stage */

      stage('Deploy') {
        when {
          anyOf {
            expression { return BRANCHES_TO_DEPLOY.contains(env.BRANCH_NAME) }
            not { expression { return params.RELEASE_CHANNEL.equals('Please make a choice') } }
            expression { return params.FORCE.contains('ANDROID_BUILD_APK') }
            expression { return params.FORCE.contains('ANDROID_DEBUG_BUILD_APK') }
            expression { return params.FORCE.contains('ANDROID_BUILD_AAB') }
            expression { return params.FORCE.contains('IOS_BUILD') }
            expression { return params.FORCE.contains('IOS_SIMULATOR_BUILD') }
            expression { return params.FORCE.contains('IOS_AD_HOC') }
            expression { return params.FORCE.contains('IOS_DEBUG_AD_HOC') }
          }
        }

        stages {
          stage('Expo publish') {
            // Publish only triggers when the branch is master
            when {
              allOf {
                not {
                  expression { return params.FORCE.contains('ANDROID_BUILD_APK') }
                }
                not {
                  expression { return params.FORCE.contains('ANDROID_DEBUG_BUILD_APK') }
                }
                not {
                  expression { return params.FORCE.contains('ANDROID_BUILD_AAB') }
                }
                not {
                  expression { return params.FORCE.contains('IOS_BUILD') }
                }
                not {
                  expression { return params.FORCE.contains('IOS_SIMULATOR_BUILD') }
                }
                not {
                  expression { return params.FORCE.contains('IOS_AD_HOC') }
                }
                not {
                  expression { return params.FORCE.contains('IOS_DEBUG_AD_HOC') }
                }
                not { branch comparator: 'EQUALS', pattern: 'master' }
              } /** end: anyOf */
            } /** end: when */
            steps {
              script {
                utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL, "started publish for ${env.JOB_NAME} version ${env.VERSION} to channel ${env.RELEASE_CHANNEL} <${env.BUILD_URL}|Open>")
              }
              withDockerContainer(image: "apollo-go:${env.IMAGE_TAG}", args: "--user='root'") {
                sh """
                  cd /home/node/workspace \
                  && yarn update:${env.RELEASE_CHANNEL}
                """
              } /** end: withDockerContainer */
            } /** end: steps */
            post {
              always {
                script {
                  utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL, "finished publish for ${env.JOB_NAME} version ${env.VERSION} to channel ${env.RELEASE_CHANNEL} <${env.BUILD_URL}|Open>")
                }
              }
            } /** end: post */
          } /** end: stage */

          stage('Build') {
            // when branch is master or any of force build options in chosen
            when {
              anyOf {
                branch 'master'
                expression { return params.FORCE.contains('ANDROID_BUILD_APK') }
                expression { return params.FORCE.contains('ANDROID_DEBUG_BUILD_APK') }
                expression { return params.FORCE.contains('ANDROID_BUILD_AAB') }
                expression { return params.FORCE.contains('IOS_BUILD') }
                expression { return params.FORCE.contains('IOS_SIMULATOR_BUILD') }
                expression { return params.FORCE.contains('IOS_AD_HOC') }
                expression { return params.FORCE.contains('IOS_DEBUG_AD_HOC') }
              } /** end: anyOf */
            } /** end: when */

            environment {
              AWS_PROFILE         = 'gdb-apollo-dev'
              ARTIFACTS_S3_BUCKET = 'dev-apollo-go-mobile-artifacts'
              DATE2PATH           = sh(script: "date '+%Y/%m/%d'", returnStdout: true).trim()
            }

            steps {
              script {
                parallel(apolloGoHelpers.getBuildStages())
              } /** end: script */
            } /** end: steps */
          } /** end: stage */
        } /** end: stages */
      } /** end: stage */
    } /** end: stages */
    post {
      always {
        script { apolloGoHelpers.fixPermissions() }
      }
      success {
        script {
          if (!env.CHANGE_ID) {
            utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL, "finished build and deployment for ${env.JOB_NAME} version ${env.VERSION} to channel ${env.RELEASE_CHANNEL} <${env.BUILD_URL}|Open>")
          } else {
            utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL, "finished PR build for ${env.JOB_NAME} version ${env.VERSION} <${env.BUILD_URL}|Open>")
          }
        } /** end: script */
      } /** end: success */
      unsuccessful {
        script {
          if (!env.CHANGE_ID) {
            utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL, "failed build and deployment for ${env.JOB_NAME} version ${env.VERSION} to channel ${env.RELEASE_CHANNEL} <${env.BUILD_URL}|Open>")
          } else {
            utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL, "failed PR build for ${env.JOB_NAME} version ${env.VERSION} <${env.BUILD_URL}|Open>")
          }
        } /** end: script */
      } /** end: unsuccessful */
    } /** end: post */
  } /** end: pipeline */
}
