/*
Config mandatory properties:
project

Config optional properties:
projectGroup
awsProfile
awsAccountId
awsRegion
ecrRepositoryName
octopusProject
notificationsChannel
deployEnv
software_catalog_enabled
softwareCatalogFiles
*/
def call(body) {
  def config = [:]
  body.resolveStrategy = Closure.DELEGATE_FIRST
  body.delegate = config
  body()

  if (!config.project) {
    error 'project is mandatory'
  }

  if (!config.containsKey("softwareCatalogFiles"))     {config.softwareCatalogFiles = []}
  if (!config.containsKey("software_catalog_enabled")) {config.software_catalog_enabled = true}

  def bumpVersionBranch = 'master'

  pipeline {
    agent {
      label 'linux-agents'
    }
    options {
      ansiColor('xterm')
      disableConcurrentBuilds()
      timeout(60)
      timestamps()
      buildDiscarder logRotator(artifactDaysToKeepStr: '',
                                artifactNumToKeepStr: '',
                                daysToKeepStr: '',
                                numToKeepStr: '30')
    }
    environment {
      PROJECT       = "${config.project}"
      PROJECT_GROUP = "${config.containsKey('projectGroup') ? config.projectGroup : 'apollo'}"

      AWS_PROFILE    = "${config.containsKey('awsProfile') ? config.awsProfile : 'gdb-apollo-dev'}"
      AWS_ACCOUNT_ID = "${config.containsKey('awsAccountId') ? config.awsAccountId : '725764004555'}"
      AWS_REGION     = "${config.containsKey('awsRegion') ? config.awsRegion : 'us-east-1'}"

      ECR_REGISTRY        = "${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com"
      ECR_REPOSITORY_NAME = "${config.containsKey('ecrRepositoryName') ? config.ecrRepositoryName : "${env.PROJECT_GROUP}/${env.PROJECT}"}"
      ECR_REGISTRY_URI    = "${env.ECR_REGISTRY}/${env.ECR_REPOSITORY_NAME}"

      OCTOPUS_PROJECT     = "${config.containsKey('octopusProject') ? config.octopusProject : "${env.PROJECT_GROUP}.${env.PROJECT}"}"
      OCTOPUS_CLI_SERVER  = 'https://octopus.delphi.zone'
      OCTOPUS_CLI_API_KEY = utility.getSecretValueById('infra/jenkins/octopus_api_key', 'gdb-delphi-dev')
      OCTOPUS_CHANNEL     = octopus.setOctoChannel(env.BRANCH_NAME)
      DEPLOY_ENV          = octopus.setDeploymentEnvMultiTenant(params.DEPLOY_ENV_CHOICE,
                                                                env.BRANCH_NAME)

      NOTIFICATIONS_CHANNEL = "${config.containsKey('notificationsChannel') ? config.notificationsChannel : '#apollo-deployments'}"

      PYPI_INDEX_URL = 'https://artifacts.apollo.stream/repository/pypi-all/simple'

      MYSQL_IMAGE = '483193324480.dkr.ecr.us-east-1.amazonaws.com/mysql:5.6'
      REDIS_IMAGE = '483193324480.dkr.ecr.us-east-1.amazonaws.com/redis:3.2.10'
    }

    parameters {
      separator(name: "separator-${config.project}-deployment",
                sectionHeader: 'Deployment parameters')
      choice(
        choices: config.containsKey('deployEnv') ? config.deployEnv : [
          'default',
          'dev',
          'test',
          'qa',
          'stage',
          'uat',
          'prod',
          'skip_deployment'
        ],
        description: 'Set environment for deployment.',
        name: 'DEPLOY_ENV_CHOICE'
      )
      choice(
        choices: [
          'default',
          'yes',
          'no'
        ],
        description: 'By default deploy is only triggered for develop/release/master branch',
        name: 'TRIGGER_DEPLOY'
      )
      choice(
        choices: [
          'error',
          'overwrite',
          'skip_build'
        ],
        description: "Docker image upload mode - ovewrite if the image with such tag already exists, raise error if exists, or skip build part if exists.",
        name: 'IMAGE_UPLOAD_MODE'
      )
      choice(
        choices: [
          'yes',
          'no',
        ],
        description: "Don't raise errors in case release already exists",
        name: 'CREATE_OCTOPUS_RELEASE'
      )
      separator(name: "separator-${config.project}-checks",
                sectionHeader: 'Skip static checks')
      booleanParam(name: 'SKIP_TEST',
                   defaultValue: config.skip_test ?: false,
                   description: 'Skip unit tests')
      booleanParam(name: 'SKIP_STATIC',
                   defaultValue: config.skip_static ?: false,
                   description: 'Skip static checks')
      booleanParam(name: 'SONAR_SCAN',
              defaultValue: false,
              description: 'Run SonarQube checks')
      booleanParam(name: 'FORCE_BUILD',
              defaultValue: false,
              description: 'Run force build if bump verison')
      booleanParam(name: 'SOFTWARE_CATALOG_ENABLED',
              defaultValue: config.software_catalog_enabled,
              description: 'Whether or not to validate/publish a Software Catalog metadata file to Datadog. Publishing will only happen if running on the master/release branches.'
      )
      booleanParam(name: 'BUMP_VERSION',
                defaultValue: true,
                description: 'Enables automatic version bump for merges into the master branch'
      )
    }

    stages {
      stage('Wrapper') {
        when {
          not {
            allOf {
              changelog 'Bump version'
              branch "${bumpVersionBranch}"
              expression { return params.FORCE_BUILD == false }
            }
          }
        }
        stages {
          stage('Bump Version') {
            when {
              allOf {
                expression { return params.BUMP_VERSION == true }
                triggeredBy cause: 'BranchEventCause'
                branch "${bumpVersionBranch}"
              }
            }
            stages {
              stage('Patch') {
                when {
                  changelog '#patch'
                }
                steps {
                  sh 'make version/patch'
                }
              } /** end: stage */

              stage('Minor') {
                when {
                  anyOf {
                    changelog '#minor'
                    branch "${bumpVersionBranch}"
                  }
                }
                steps {
                  sh 'make version/minor'
                }
              } /** end: stage */

              stage('Major') {
                when {
                  changelog '#major'
                }
                steps {
                  sh 'make version/major'
                }
              } /** end: stage */
            } /** end: stages */
            post {
              success {
                script {
                  sshagent(['theorchard-ci-ssh-key']) {
                    sh """
                      #!/usr/bin/env bash
                      set +x
                      export GIT_SSH_COMMAND="ssh -oStrictHostKeyChecking=no"
                      git push origin HEAD:${env.BRANCH_NAME}
                      git push origin v\$(cat VERSION)
                    """
                  } /** end: sshagent */
                } /** end: script */
              } /** end: success */
            } /** end: post */
          } /** end: stage */

          stage('Validate Software Catalog Definition') {
            when {
              expression { return params.SOFTWARE_CATALOG_ENABLED}
            }
            steps {
              library "jenkins-global-libraries@master"
              datadogSoftwareCatalogValidate()
              script {
                config.softwareCatalogFiles.each {file ->
                  datadogSoftwareCatalogValidate(
                    serviceDefinitionFilePath: file,
                  )
                }
              }
            }
          }

          stage('BuildWrapper') {
            environment {
              VERSION = octopus.getVersion(env.BRANCH_NAME,env.WORKSPACE)
            }

            stages {
              stage('Build') {
                options { retry(2) }
                steps {
                  script { currentBuild.displayName = env.VERSION }
                  sh label: 'build', script: '''
                    cp .env.sample .env
                    make docker/image/build
                  '''
                } /** end: steps */
              } /** end: stage */

              stage('Test') {
                options { retry(2) }
                when {
                  not {
                    expression { return params.SKIP_TEST }
                  }
                }
                steps {
                  sh label: 'test', script: '''
                    make docker/test
                  '''
                } /** end: steps */
                post {
                  always {
                    sh "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
                    junit allowEmptyResults: false, testResults: "report.xml"
                    recordCoverage(
                      tools: [[parser: 'COBERTURA', pattern: 'coverage.xml']],
                      enabledForFailure: true,
                      failOnError: true,
                      sourceDirectories: [[path: "."]]
                    )
                    sh 'make docker/clean'
                  }
                }
              } /** end: stage */

              stage('Static') {
                when {
                  not {
                    expression { return params.SKIP_STATIC }
                  }
                }
                parallel {
                  stage('Checkstyle') {
                    steps {
                      sh label: 'checkstyle', script: '''
                        make docker/checkstyle
                      '''
                    } /** end: steps */
                    post {
                      always {
                        sh "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
                      }
                    }
                  } /** end: stage */
                  stage('Security') {
                    steps {
                      sh label: 'check dependencies', script: '''
                        make docker/security
                      '''
                    } /** end: steps */
                    post {
                      always {
                        sh "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
                      }
                    }
                  } /** end: stage */
                } /** end: parallel */
              } /** end: stage */

              stage('SonarQube Analysis') {
                when {
                  allOf {
                    branch 'develop' // there is a limit to a single branch in the sonarqube community edition
                    expression { return params.SONAR_SCAN }
                  }
                }
                environment {
                  SONAR_HOST_URL         = "https://sonar.delphiplatform.io"
                  SONAR_PROJECT_BASE_DIR = "${env.WORKSPACE}"
                  SONAR_TOKEN            = utility.getSecretValueById('infra/jenkins/common/sonar_login_token', 'gdb-infra-dev')
                }
                steps {
                  script {
                    docker.image("sonarsource/sonar-scanner-cli").inside {
                      sh script: """
set +x
sonar-scanner \
  -Dsonar.projectKey=${env.PROJECT_GROUP}.${env.PROJECT} \
  -Dsonar.projectBaseDir=${env.SONAR_PROJECT_BASE_DIR} \
  -Dsonar.host.url=${env.SONAR_HOST_URL} \
  -Dsonar.login=${env.SONAR_TOKEN} \
  -Dsonar.projectVersion=${env.VERSION} \
"""
                    }
                  }
                } /** end: steps */
              } /** end: stage */

              stage('Deploy') {
                when {
                  anyOf {
                    branch 'master'
                    branch 'develop'
                    branch comparator: 'GLOB', pattern: 'hotfix/*'
                    branch comparator: 'GLOB', pattern: 'hotfix*'
                    expression { return params.TRIGGER_DEPLOY.equals('yes') }
                  }
                }
                stages {
                  stage('Check version') {
                    when {
                      expression {
                        return params.IMAGE_UPLOAD_MODE == 'error' && octopus.ecrImageTagExists(env.VERSION,
                                                                                                env.ECR_REPOSITORY_NAME,
                                                                                                env.AWS_PROFILE)
                      }
                    }
                    steps { error "Image with ${env.VERSION} version tag already exists. To ignore this error set IMAGE_UPLOAD_MODE parameter to overwrite or skip_build"}
                  } /** end: stage */
                  stage('Push to ECR') {
                    when {
                      expression {
                        return params.IMAGE_UPLOAD_MODE == 'overwrite' || !octopus.ecrImageTagExists(env.VERSION,
                                                                                                     env.ECR_REPOSITORY_NAME,
                                                                                                     env.AWS_PROFILE)
                      }
                    }
                    stages {
                      stage('Scan image') {
                        steps {
                          script {
                            octopus.prismaCloudScanImage(env.VERSION,
                                                         env.ECR_REPOSITORY_NAME,
                                                         env.AWS_PROFILE)
                          }
                        } /** end: steps */
                      } /** end: stage */
                      stage('Push image') {
                        steps {
                          sh label: 'push', script: '''
                            make docker/image/push
                          '''
                        } /** end: steps */
                      } /** end: stage */
                    } /** end: stages */
                  } /** end: stage */
                  stage('Upload documentation') {
                    when {
                      allOf {
                        expression { return fileExists("${env.WORKSPACE}/docs") }
                        anyOf {
                          branch 'master'
                          branch 'develop'
                        }
                      }
                    }
                    steps {
                      script {
                        documentation.upload(env.BRANCH_NAME, env.WORKSPACE)
                      } /** end: script */
                    } /** end: steps */
                  } /** end: stage */
                  stage('Create a release in octopus') {
                    //Check if release already exists. If it does, raise error or skip it, based on given param
                    when {
                      expression { return params.CREATE_OCTOPUS_RELEASE.equals('yes') }
                    }
                    steps {
                      echo octopus.createRelease(env.OCTOPUS_PROJECT,
                                                 env.VERSION,
                                                 env.OCTOPUS_CHANNEL)
                    } /** end: steps */
                  } /** end: stage */
                  stage('Deploy release in octopus') {
                    when {
                      not { expression { return env.DEPLOY_ENV.equals('skip_deployment') } }
                    }
                    steps {
                      echo octopus.deployReleaseToAllTenants(env.OCTOPUS_PROJECT,
                                                 env.DEPLOY_ENV,
                                                 env.VERSION,
                                                 env.OCTOPUS_CHANNEL)
                    } /** end: steps */
                  } /** end: stage */
                } /** end: stages */
              } /** end: stage */
            } /** end; stages */
          } /** end; stage */
          stage('Publish Software Catalog Definition') {
            when {
              allOf{
                branch 'master'
                expression { return params.SOFTWARE_CATALOG_ENABLED}
              }
            }
            steps{
              datadogSoftwareCatalogPublish(
                fetchAdditionalMetadata: false,
              )
              script {
                config.softwareCatalogFiles.each {file ->
                  datadogSoftwareCatalogPublish(
                    serviceDefinitionFilePath: file,
                    fetchAdditionalMetadata: false,
                  )
                }
              }
            }
          } /** end: stage */
        } /** end: stages */
        post {
          always {
            sh label: 'fix permissions', script: "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
            sh label: 'shutdown containers', script: 'make docker/clean'
            script { utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL) }
          }
          cleanup { deleteDir() }
        } /** end: post */
      } /** end: stage */
    } /** end: stages */
  } /** end: pipeline */
}
