/*
Config mandatory properties:
project

Optional properties:
skip_image_scan
slackChannel
awsProfile
awsRegion
ecrRepositoryName
software_catalog_enabled
*/
def call(body) {
  def config = [:]
  body.resolveStrategy = Closure.DELEGATE_FIRST
  body.delegate = config
  body()
  def optional_params = []
  if ( !config.containsKey("slackChannel") )   {config.slackChannel   = "crm-cicd-alerts"}
  if ( !config.containsKey("awsProfile") )     {config.awsProfile     = "gdb-prodhub-dev"}
  if (!config.containsKey("software_catalog_enabled")) {config.software_catalog_enabled = true}

  def mandatory_params = [
    booleanParam(name: 'SKIP_CI_REPORTS',
                defaultValue: false,
                description: "Skip checkstyle, pmd and findbugs reports"),
    booleanParam(name: 'SKIP_IMAGE_SCAN',
              defaultValue: config.skip_image_scan ?: false,
              description: 'Skip docker image scan'),
    booleanParam(name: 'TRIGGER_DEPLOY',
              defaultValue: false,
              description: '''Whether to trigger deployment pipeline(create octopus release, deploy it).
              By default it\'s only master branch.
              '''),
    choice(
      choices: [
        'default',
        'dev',
        'skip_deployment'
      ],
      description: 'Set environment for deployment. Skipped by default master/release*, dev by default for all other branches',
      name: 'DEPLOY_ENV_CHOICE'
    ),
    choice(name: 'IMAGE_UPLOAD_MODE',
          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."),
    booleanParam(name: 'FAIL_IF_RELEASE_EXISTS',
                defaultValue: true,
                description: "Don't raise errors in case release already exists"),
    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 branch.'
      )
  ]
  properties([parameters(( mandatory_params << optional_params).flatten() )])

  pipeline {
    agent {
      label 'linux-agents'
    }
    options { disableConcurrentBuilds() }
    environment {
      PROJECT       = "${config.project}"
      PROJECT_GROUP = "crm"
      OCTOPUS_PROJECT = "${env.PROJECT_GROUP}.${env.PROJECT}"

      AWS_PROFILE    = "${config.containsKey('aws_profile') ? config.aws_profile : "gdb-prodhub-dev"}"
      AWS_ACCOUNT_ID = "${utility.getAccountIdByProfile(env.AWS_PROFILE)}"
      AWS_REGION     = "${config.containsKey('aws_region') ? config.aws_region : 'us-east-1'}"

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

      OCTOPUS_CLI_SERVER      = "https://octopus.delphi.zone"
      OCTOPUS_CLI_API_KEY     = sh(script: "aws secretsmanager get-secret-value --profile gdb-delphi-dev --secret-id 'infra/jenkins/octopus_api_key' --output json | jq -r .SecretString", , returnStdout: true).trim()
      OCTOPUS_CHANNEL         = octopus.setOctoChannel(env.BRANCH_NAME)
      DEPLOY_ENV              = octopus.setDeploymentEnvCRM(params.DEPLOY_ENV_CHOICE, env.PROJECT_GROUP, env.BRANCH_NAME )
      NOTIFICATIONS_CHANNEL   = "${config.slackChannel}"
    }
    stages {
      stage("Set AWS credentials") {
        steps {
          script {
            utility.assumeDeploymentRole(env.AWS_ACCOUNT_ID, "jenkins-${config.project}")
          }
        }
      }
      stage("Maven") {
        agent {
          docker {
            image "maven:3.8.6-jdk-8-slim"
            // The following args are aimed to workaround some issues with using Docker container as a Jenkins agent:
            // 1. Disable image entrypoint to let Jenkins verify container was started correctly using custom commands.
            // 2. Explicitly tell SBT locations for configs and local cache because Jenkins runs container using a
            //    custom user (with `-u 1000:1000`) and SBT cannot resolve these paths automatically.
            // 3. Set MaxMetaspaceSize to 1G due to OOM
            //args '--entrypoint "" -e JAVA_OPTS="-Dsbt.color=false -Dsbt.global.base=.sbt -Dsbt.boot.directory=.sbt -Dsbt.ivy.home=.ivy2"'
            reuseNode true
          }
        }
        stages {
          stage("Dependencies") {
            steps {
              sh 'mvn dependency:go-offline -B --settings .custom-mvn-settings.xml'
            }
          }
          stage("Test") {
            steps {
              sh 'mvn test -q --settings .custom-mvn-settings.xml'
            }
          }
          stage("Checkstyle/pmd") {
            when {
              not {
                expression {return params.SKIP_CI_REPORTS}
              }
            }
            steps {
              sh 'mvn checkstyle:checkstyle pmd:pmd pmd:cpd -q --settings .custom-mvn-settings.xml'
            }
          }
        }
        post {
          always {
            junit testResults: '**/target/surefire-reports/TEST-*.xml'
            recordIssues enabledForFailure: false, tools: [mavenConsole(), java(), javaDoc()]
            recordIssues enabledForFailure: true, tool: checkStyle()
            recordIssues enabledForFailure: true, tool: cpd(pattern: '**/target/cpd.xml')
            recordIssues enabledForFailure: true, tool: pmdParser(pattern: '**/target/pmd.xml')
          }
        }
      }

      stage('Deploy') {
        when {
          anyOf {
            branch 'master'
            expression { return params.TRIGGER_DEPLOY }
          }
        }
        environment {
          WORKING_DIRECTORY = '.'
          VERSION = octopus.getVersion(env.BRANCH_NAME, env.WORKING_DIRECTORY)
        }
        stages {
          stage('Validate Software Catalog Definition') {
            when {
              expression { return params.SOFTWARE_CATALOG_ENABLED}
            }
            steps {
              library "jenkins-global-libraries@master"
              datadogSoftwareCatalogValidate()
            }
          }
          stage('Build') {
            steps {
              sh 'make docker/build'
            }
          }
          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"}
          }
          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') {
                when {
                  not {
                    expression { return params.SKIP_IMAGE_SCAN }
                  }
                }
                steps {
                  script {
                    octopus.prismaCloudScanImage(env.VERSION, env.ECR_REPOSITORY_NAME, env.AWS_PROFILE)
                  }
                }
              }
              stage('Push image') {
                steps {
                  sh label: 'push', script: """
                      cd ${env.WORKING_DIRECTORY} \
                      && make docker/push
                  """
                }
              }
            }
          }
          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.SKIP_RELEASE_CREATION.equals('no') || !octopus.releaseExists(env.OCTOPUS_PROJECT,env.VERSION) }
            }
            steps {
              echo octopus.createRelease(env.OCTOPUS_PROJECT, env.VERSION, env.OCTOPUS_CHANNEL)
            }
          }
          stage('Deploy release in octopus') {
            when {
              not { expression { return env.DEPLOY_ENV.equals('skip_deployment') } }
            }
            steps {
              echo octopus.deployRelease(env.OCTOPUS_PROJECT, env.DEPLOY_ENV, env.VERSION, env.OCTOPUS_CHANNEL)
            }
          }
          stage('Publish Software Catalog Definition') {
            when {
              allOf{
                branch 'master'
                expression { return params.SOFTWARE_CATALOG_ENABLED}
              }
            }
            steps{
              datadogSoftwareCatalogPublish(
                fetchAdditionalMetadata: false,
              )
            }
          }
        }
      }
    }
    post {
      always {
        sh "sudo chown -R ec2-user:ec2-user ${env.WORKSPACE}"
      }
      success {
        script {
        if (currentBuild.getPreviousBuild() &&
            currentBuild.getPreviousBuild().getResult().toString() != "SUCCESS") {
          utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL)
        }
        }
      }
      failure {
        script {
          utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL)
        }
      }
      cleanup {
          deleteDir()
      }
    }
  }
}
