@Library('nf-jenkins-shared-libraries@master')_
import org.apache.commons.lang3.StringUtils;
import jenkins.model.Jenkins
import hudson.model.*
import hudson.tasks.test.*
import groovy.xml.XmlUtil
import org.json.JSONObject
import org.json.XML

service_repo = []
IGNORE_TITLES = []
FLYWAY_VALIDATION_CHECK_IGNORE = []

MVN_HOME = ""
BB_BRANCH = ""
COMMIT_HASH = ""

REPO = "core-management"

kibana_url = ""
smoketest_url = ""
artifact_link = ""
sonar_link = ""
failure_msg = ""
sandbox_image = ""
stage_name = ""
link_url = ""

failure = false
blocking = true
send_notification = true
sonar_alert = false
killed_running_job = false
set_kibana_url = false
set_affected_service = false
set_smoketest_url = false
set_artifact_url = false
affected_service = null
flywaymigration_detected = false

pipeline {
    agent {
        ecs {
            inheritFrom 'agent-jdk17'
            cpu 4096
            memoryReservation 16384
        }
    }

    environment {
        BITBUCKET_AUTH = credentials('bitbucket_auth')
        BITBUCKET_ADMIN = credentials('bitbucket_admin')
    }

    stages {
        stage('Initialize') {
            steps {
                script {
                    stage_name = env.STAGE_NAME
                    bb_request = readJSON text: bb_request_raw
                    REPO = bb_request.pullrequest.source.repository.full_name.split("/")[1]
                    BB_COMMENTS_URL="${bb_request.pullrequest.links.comments.href}"
                    BB_APPROVE_URL="${bb_request.pullrequest.links.approve.href}"
                    BB_DECLINE_URL="${bb_request.pullrequest.links.decline.href}"
                    BB_BRANCH="${bb_request.pullrequest.source.branch.name}"
                    BB_DESTINATION_BRANCH="${bb_request.pullrequest.destination.branch.name}"
                    COMMIT_HASH = "${bb_request.pullrequest.source.commit.hash}"
                    DESCRIPTION="Branch: ${BB_BRANCH} REPO: ${REPO}"

                    // Set Description
                    currentBuild.description = "${DESCRIPTION}"

                    //fetch constants
                    pr_const = readYaml text: libraryResource("pr_builds/const_vars.yaml")

                    FLYWAY_VALIDATION_CHECK_IGNORE = pr_const["FLYWAY_VALIDATION_CHECK_IGNORE"]
                    MOP_LIBRARIES = pr_const["MOP_LIBRARIES"]
                    SONAR_EXEMPTION = pr_const["SONAR_EXEMPTION"]
                    MVN_HOME = pr_const["MVN_HOME"]
                    JAVA_HOME='/opt/java/openjdk'
                    CLUSTER_NAME = pr_const["CLUSTER_NAME"]
                    HELM_TIMEOUT = pr_const["HELM_TIMEOUT"]
                    helm_verbose_level = pr_const["helm_verbose_level"]
                    prenv = pr_const["prenv"]
                    channel = pr_const["channel"]
                    log_lines_capture = pr_const["log_lines_capture"]
                    update_jenkins_build = pr_const["update_jenkins_build"]

                    service_repo = readYaml text: libraryResource("pr_builds/service_repo_list.yaml")
                    IGNORE_TITLES = readYaml text: libraryResource("pr_builds/ignore_titles.yaml")

                    for(String ignoreStr: IGNORE_TITLES) {
                        if(bb_request.pullrequest.rendered.title.raw.toUpperCase().contains("${ignoreStr}")) {
                            failure_msg = "ignore"
                            send_notification = false
                            error "Build ignored as it starts with ${ignoreStr}"
                        }
                    }
                }
            }
        }

        stage('ChangeSet'){
            steps {
                script {
                    stage_name = env.STAGE_NAME
                    git_diff_serverless(REPO, BB_BRANCH)

                    // Check that destination branch
                    if(BB_DESTINATION_BRANCH != "develop") {
                        failure_msg = "ignore"
                        error "Only builds PR with destination branch as develop"
                    }

                    //detect flyway changes
                    flywaymigration_detected = gitDetectFlywayChanges()

                    // Abort prev running build and wait for cleanup
                    //abortPreviousRunningBuilds()
                    //sleep 60
                    bitbucketStatus repo: "${REPO}", BRANCH_NAME: BB_BRANCH, GIT_COMMIT: COMMIT_HASH, status: "INPROGRESS", PR_CHECK: "false", BUILD_NAME: "Jenkins PR Build"
                }
            }
        }

        stage('Build') {
            steps {
                script {
                    stage_name = env.STAGE_NAME
                    check_dependency_mvnw_serverless("${REPO}", "${REPO}", MOP_LIBRARIES)
                    configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                        sh """
                        export SPRING_PROFILES_ACTIVE=test
                        ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 clean
                        ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -DskipTests compile test-compile
                        """
                    }
                }
            }
        }
        
        stage('Maven Tests') {
            parallel {
                stage('Baseline Tests') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                export SPRING_PROFILES_ACTIVE=test
                                ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -Ptest \
                                -Dtest.redis.port=6400 -Dlogging.level.io.netfoundry=warn -Dlogging.level.io.netfoundry.core=info -Dlogging.level.io.netfoundry.common=info -Dlogging.level.org.springframework=warn \
                                test -Dgroups=\"!validation & !find & !controller_common & !controller_v2 & !controller_v3 & !process\"
                                """
                            }
                        }
                    }
                }

                stage('Validation Tests') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                export SPRING_PROFILES_ACTIVE=test
                                ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -Ptest \
                                -Dtest.redis.port=6404 -Dlogging.level.io.netfoundry=warn -Dlogging.level.io.netfoundry.core=info -Dlogging.level.io.netfoundry.common=info -Dlogging.level.org.springframework=warn \
                                test -Dgroups=\"validation\"
                                """
                            }
                        }
                    }
                }

                stage('Controller Tests (Common)') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                export SPRING_PROFILES_ACTIVE=test
                                ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -Ptest \
                                -Dtest.redis.port=6401 -Dlogging.level.io.netfoundry=warn -Dlogging.level.io.netfoundry.core=info -Dlogging.level.io.netfoundry.common=info -Dlogging.level.org.springframework=warn \
                                test -Dgroups=\"controller_common\"
                                """
                            }
                        }
                    }
                }

                stage('Controller Tests (V2)') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                export SPRING_PROFILES_ACTIVE=test
                                ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -Ptest \
                                -Dtest.redis.port=6402 -Dlogging.level.io.netfoundry=warn -Dlogging.level.io.netfoundry.core=info -Dlogging.level.io.netfoundry.common=info -Dlogging.level.org.springframework=warn \
                                test -Dgroups=\"controller_v2\"
                                """
                            }
                        }
                    }
                }

                stage('Controller Tests (V3)') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                export SPRING_PROFILES_ACTIVE=test
                                ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -Ptest \
                                -Dtest.redis.port=6403 -Dlogging.level.io.netfoundry=warn -Dlogging.level.io.netfoundry.core=info -Dlogging.level.io.netfoundry.common=info -Dlogging.level.org.springframework=warn \
                                test -Dgroups=\"controller_v3\"
                                """
                            }
                        }
                    }
                }
            }
        }

        stage('Find and Process Tests') {
            parallel {
                stage('Find Tests') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                export SPRING_PROFILES_ACTIVE=test
                                ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -Ptest \
                                -Dtest.redis.port=6405 -Dlogging.level.io.netfoundry=warn -Dlogging.level.io.netfoundry.core=info -Dlogging.level.io.netfoundry.common=info -Dlogging.level.org.springframework=warn \
                                test -Dgroups=\"find\"
                                """
                            }
                        }
                    }
                }

                stage('Process Tests') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                export SPRING_PROFILES_ACTIVE=test
                                ./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -T 8 -Ptest \
                                -Dtest.redis.port=6406 -Dlogging.level.io.netfoundry=warn -Dlogging.level.io.netfoundry.core=info -Dlogging.level.io.netfoundry.common=info -Dlogging.level.org.springframework=warn \
                                test -Dgroups=\"process\"
                                """
                            }
                        }
                    }
                }
            }
        }

        stage('Docs') {
            parallel {
                stage('Docs') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh "./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -Pdocs -DskipTests verify"
                            }
                        }
                    }
                }

                stage('OpenApi Client') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh """
                                ./mvnw -s $MAVEN_SETTINGS_XML resources:resources generate-sources -PcoreClient
                                ./mvnw -s $MAVEN_SETTINGS_XML -f target/client/pom.xml -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} install
                                ./mvnw -s $MAVEN_SETTINGS_XML -f target/admin-client/pom.xml -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} install
                                """
                            }
                        }
                    }
                }
            }
        }

        stage('Sonar and Dependency Checks') {
            parallel {
                stage('Dependency Checks') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            try{
                                configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                    sh "./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -Pdependency_check -DskipTests dependency-check:check xml:transform"
                                }
                            }catch(e){
                                echo "Dependency Checks failed with error ${e}"
                                fn_msg = "Dependency Checks failed for branch ${bb_request.pullrequest.source.branch.name}. The build will still continue. Please check link for details."
                                color = "#FF8000"
                                author_email = fetch_author_email("${REPO}", "${COMMIT_HASH}")
                                send_custom_notification("${author_email}", "${REPO}", "${channel}", "${fn_msg}", "${color}", "${link_url}")
                            }
                        }
                    }
                }

                stage('Sonar Quality Gate') {
                    when { expression { newCheckAllowedServices(bb_request, SONAR_EXEMPTION, REPO) == true }}
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            JAVA_HOME = "${JAVA_HOME}"
                            
                            //generate jacoco report
                            configFileProvider([configFile(fileId: 'netfoundry_maven_settings', variable: 'MAVEN_SETTINGS_XML')]) {
                                sh "./mvnw -s $MAVEN_SETTINGS_XML -Dmaven.repo.local=${MVN_HOME}/${BUILD_NUMBER} -Ptest -DskipTests verify"
                            }

                            //sonar analysis
                            sonar_link = check_sonar_quality_gate_mvnw(bb_request, SONAR_EXEMPTION, REPO, BB_BRANCH, MVN_HOME, JAVA_HOME)
                            if( sonar_link != "") {
                                sonar_alert = true
                                error "Sonar Analysis failed. Link -> ${sonar_link}"
                            }
                        }
                    }
                }
            }
        }

        stage('Flyway Migration') {
            when { expression {flywaymigration_detected == true}}
            steps {
                script {
                    stage_name = env.STAGE_NAME
                    ignore_flyway_validation = false
                    for(String ignoreStr1: FLYWAY_VALIDATION_CHECK_IGNORE) {
                        if(bb_request.pullrequest.rendered.title.raw.toUpperCase().contains("${ignoreStr1}")) {
                            ignore_flyway_validation = true
                        }
                    }
                    def build_job_result = build job: "flyway_migration_prbuilds", parameters: [
                        [$class: 'StringParameterValue', name: 'REPO', value: "${REPO}"],
                        [$class: 'StringParameterValue', name: 'environment', value: "${prenv}"],
                        [$class: 'StringParameterValue', name: 'BRANCH', value: "${BB_BRANCH}"],
                        [$class: 'BooleanParameterValue', name: 'ignore_flyway_validate', value: ignore_flyway_validation]
                    ], propagate: true, wait: true
                }
            }
        }
    }

    post {
        always {
            script {
                echo "build completed"
                sh "rm -rf ${MVN_HOME}/${BUILD_NUMBER}"
            }
        }
        failure {
            script {
                failure_reason = "${stage_name} stage failed. ${failure_msg}"
                if (failure_msg != "ignore"){
                    bbReject(bb_request, REPO, BB_BRANCH, COMMIT_HASH, failure_reason, update_jenkins_build, affected_service, set_kibana_url, kibana_url, set_smoketest_url, smoketest_url, set_artifact_url, artifact_link, sonar_alert, sonar_link)
                }
                //Notification
                if(failure_msg != "ignore" && failure_msg != "org.jenkinsci.plugins.workflow.steps.FlowInterruptedException" && send_notification == true ){
                    pr_send_notification_message(bb_request, "${REPO}", failure_reason, channel, get_notification_links(bb_request, affected_service, set_kibana_url, kibana_url, set_smoketest_url, smoketest_url, set_artifact_url, artifact_link, sonar_alert, sonar_link))
                    currentBuild.rawBuild.@result = hudson.model.Result.FAILURE
                }
                if(failure_msg == "ignore") {
                    bitbucketStatus repo: "${REPO}", BRANCH_NAME: BB_BRANCH, GIT_COMMIT: COMMIT_HASH, status: "SUCCESSFUL", PR_CHECK: "false", BUILD_NAME: "Jenkins PR Build"
                    currentBuild.rawBuild.@result = hudson.model.Result.ABORTED
                }
            }
        }
        success {
            script {
                //Notification
                if (failure_msg != "ignore"){
                    bbApprove(bb_request, REPO, BB_BRANCH, COMMIT_HASH)
                }
                if(failure_msg == "ignore") {
                    bitbucketStatus repo: "${REPO}", BRANCH_NAME: BB_BRANCH, GIT_COMMIT: COMMIT_HASH, status: "SUCCESSFUL", PR_CHECK: "false", BUILD_NAME: "Jenkins PR Build"
                    currentBuild.rawBuild.@result = hudson.model.Result.ABORTED
                }
            }
        }
    }
}
