@Library('nf-jenkins-shared-libraries')_  
import jenkins.model.Jenkins
import org.apache.commons.lang3.StringUtils;
import hudson.model.*

repo = "admin-gui"
apiUrl = "https://bitbucket.org/api"
error_msg = null
prodbranch = "prodrelease"
commitHash = ""
channel = "cd-ui-staging"
def dep_environment = "sandbox"
startedUserId = ""
def cfinvalidation
def CF_DISTRIBUTION =  [ 'sandbox': 'E2XXWR5XCP9VOX', 'staging': 'ESJT33GN4NMB1', 'production': 'E2PDVWYFH4TD0H' ]
authorized_users = ["nick.pieros@netfoundry.io", "loren.fouts@netfoundry.io", "vinay.lakshmaiah@netfoundry.io", "mike.guthrie@netfoundry.io", "ryan.galletto@netfoundry.io", "harish.donepudi@netfoundry.io"]
MattermostEmail = ['mike.guthrie@netfoundry.io': 'mike.guthrie', 'nick.pieros@netfoundry.io': 'nick.pieros', 'loren.love@netfoundry.io': 'loren.love', 'vinay.lakshmaiah@netfoundry.io': 'vinay.lakshmaiah', 'ryan.galletto@netfoundry.io': 'ryan.galletto', 'harish.donepudi@netfoundry.io': 'harish.donepudi']
MattermostNames = ['Mike Guthrie': 'mike.guthrie', 'Nick Pieros': 'nick.pieros', 'Loren Love': 'loren.love', 'Vinay Lakshmaiah': 'vinay.lakshmaiah', 'Ryan Galletto': 'ryan.galletto', 'Harish Donepudi': 'harish.donepudi']

pipeline {
    agent {
        dockerfile { 
            filename 'Dockerfile'
            args '-u 0 --privileged'
        }
    }
    
    environment {
        DOCKER_HUB = credentials('dockerhub')
        BITBUCKET_AUTH = credentials('bitbucket_auth')
        BITBUCKET_ADMIN = credentials('bitbucket_admin')
    }

    parameters {
        booleanParam(name: 'promote', defaultValue: false, description: 'Promote Container')
        choice(name:'environment', choices: "staging\nproduction", description: 'Environment to Deploy To')
    }

    stages {
        stage('Build') {
            steps {
                script {
                    try {
                        bitbucketStatus repo: "${repo}", BRANCH_NAME: "${BRANCH_NAME}", GIT_COMMIT: "${GIT_COMMIT}", PR_CHECK: "false", status: "INPROGRESS"
                        sh "npm install"
                        sh "node version.js"
                        sh "npm run lint"
                        sh "npm run test"
                        sh "npm run build-aot"

                        archiveArtifacts "node_modules/sonar-scanner/**"
                    } catch(e){
                        error_msg = "Build Failed for branch ${BRANCH_NAME}"
                        error "${error_msg}"
                    }
                }
            }
        }

        stage('Deployment to Sandox') {
            when { expression { BRANCH_NAME == "develop" }}
            steps {
                script {
                    try{
                        dep_environment = "sandbox"
                        sh "npm install"
                        sh "node version.js"
                        sh "npm test"
                        sh "node --max_old_space_size=12000 ./node_modules/.bin/ng build --configuration=sandbox --prod --aot"
                    
                        archiveArtifacts "dist/**"
                    } catch(e){
                        error_msg = "Build Failed for branch ${BRANCH_NAME} during Deployment to Sandox"
                        error "${error_msg}"
                    }

                    adminGUIDeploy("${dep_environment}", "${repo}", CF_DISTRIBUTION, "${GIT_COMMIT}")

                    sh "rm -rf coverage"
                    sh "rm -rf dist"
                    sh "rm -rf node_modules"
                }
            }
        }

        stage('Promote to Staging') {
            when { expression { BRANCH_NAME == "develop" || (params.promote == true && params.environment == "staging" && BRANCH_NAME ==~ /hotfix.*/ ) }}
            steps {
                script {
                    try{
                        dep_environment = "staging"
                        currentBuild.description = "Promoting Build - Environment: ${dep_environment} REPO: ${repo} Hash: ${GIT_COMMIT}"

                        //commitHash = getBranchCommitHash("develop", repo, apiUrl)
                        //def author = fetch_author(repo, commitHash)
                        //promotion_notify(author, commitHash, dep_environment)

                        sh "npm install"
                        sh "node version.js"
                        sh "node --max_old_space_size=8000 ./node_modules/.bin/ng build --configuration=staging --prod --aot"
                    
                        archiveArtifacts "dist/**"
                    } catch(e){
                        error_msg = "Build Failed for branch ${BRANCH_NAME} during Promote to Staging"
                        error "${error_msg}"
                    }

                    adminGUIDeploy("${dep_environment}", "${repo}", CF_DISTRIBUTION, "${GIT_COMMIT}")

                    sh "rm -rf coverage"
                    sh "rm -rf dist"
                    sh "rm -rf node_modules"
                }
            }
        }

        stage('Create Prodrelease branch') {
            when { expression { BRANCH_NAME == "develop" }}
            steps {
                script {
                    commitHash = getBranchCommitHash("develop", repo, apiUrl)

                    if(checkIfBranchExists(prodbranch, repo, apiUrl)) {
                        echo "branch exist"
                        removeBranch(prodbranch, repo, apiUrl)
                    }

                    def createBranchUrl = apiUrl+"/2.0/repositories/netfoundry/${repo}/refs/branches"
                    data = [
                        "name" : "${prodbranch}",
                        "target" : [ "hash" : commitHash ]
                    ]
                    def createResult = bitbucket endpoint: createBranchUrl, method: "POST",  data: data, description: "Create Branch", username: BITBUCKET_AUTH_USR, password: BITBUCKET_AUTH_PSW, output: "true"
                    if ( createResult["resultCode"] > 300) {
                        error_msg = "Creating Branch for production release failed"
                        error("Creating Branch failed with error: "+createResult["resultCode"])
                    }
                }
            }
        }

        stage('Promote to Production') {
            when { expression { params.promote == true && params.environment == "production" }}
            steps {
                script {
                    dep_environment = "production"
                    try {
                        startedUserId = currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
                        if(! authorized_users.contains(startedUserId)){
                            error_msg = "User ${startedUserId} is not authorized to run this pipeline"
                            error "User ${startedUserId} is not authorized to run this pipeline"
                        }
                    } catch(e) {
                        echo "User ${startedUserId} not authorized to run"
                    }

                    if( "${BRANCH_NAME}" == "${prodbranch}" || "${BRANCH_NAME}" ==~ /hotfix.*/){
                        //commitHash = getBranchCommitHash(prodbranch, repo, apiUrl)
                        //promotion_notify(startedUserId, commitHash, dep_environment)

                        currentBuild.description = "Promoting Build - Environment: ${dep_environment} REPO: ${repo} Hash: ${GIT_COMMIT}"
                        try{
                            sh "npm install"
                            sh "node version.js"
                            sh "npm run build-prod"

                            archiveArtifacts "dist/**"
                        } catch(e){
                            error_msg = "Build Failed for branch ${BRANCH_NAME} during Promotion to Production"
                            error "${error_msg}"
                        }

                        adminGUIDeploy("${dep_environment}", "${repo}", CF_DISTRIBUTION, "${GIT_COMMIT}")

                        sh "rm -rf coverage"
                        sh "rm -rf dist"
                        sh "rm -rf node_modules"
                    }else{
                        error_msg = "Cannot Promote branch ${BRANCH_NAME} to production, use branch hotfix/* or ${prodbranch} to promote to production"
                        error "${error_msg}"
                    }
                }
            }
        }

        stage('BackMerge'){
            when { expression { params.promote == true && params.environment == "production" && ( BRANCH_NAME == "${prodbranch}" || BRANCH_NAME ==~ /hotfix.*/)  }}
            steps {
                script {
                    try {
                        def releaseTag = "release-${GIT_COMMIT}-${new Date().format( 'yyyyMMddHHmm')}"
                        sleep 30
                        bitbucketActions action: "merge", source_branch: "${BRANCH_NAME}", repo: repo, dest_branch: "master"
                        bitbucketActions action: "tag", source_branch: "master", repo: repo, tag_name: "${releaseTag}"

                        //No need to backmerge prodrelease and do not close the prodrelease branch
                        if( "${BRANCH_NAME}" != "${prodbranch}"){
                            bitbucketActions action: "merge", source_branch: "${BRANCH_NAME}", repo: repo, dest_branch: "develop"
                            bitbucketActions action: "close", source_branch: "${BRANCH_NAME}", repo: repo
                        }
                    } catch(e) {
                        error_msg = "Backmerge failed with error ${e}"
                        error "${error_msg}"
                    }
                }
            }
        }

        stage('Closure') {
            steps {
                script {
                    if( dep_environment != "sandbox" ){
                        send_notification(GIT_COMMIT, dep_environment)
                    }
                }
            }
        }
    }

    post {
        always {
            script{
                sh "ls -A1 | xargs rm -r"
            }
        }
        aborted {
            bitbucketStatus repo: "${repo}", BRANCH_NAME: BRANCH_NAME, GIT_COMMIT: GIT_COMMIT, PR_CHECK: "false", status: "STOPPED"
        }
        failure {
            bitbucketStatus repo: "${repo}", BRANCH_NAME: BRANCH_NAME, GIT_COMMIT: GIT_COMMIT, PR_CHECK: "false", status: "FAILED"
            script {
                send_notification(GIT_COMMIT, dep_environment)
            }
        }
        success {
            bitbucketStatus repo: "${repo}", BRANCH_NAME: BRANCH_NAME, GIT_COMMIT: GIT_COMMIT, PR_CHECK: "false", status: "SUCCESSFUL"
        }
    }
}

def promotion_notify(userid, commit_hash, environment){
    def username = ""
    if(environment == "production"){
        username = "${MattermostEmail[userid]}"
        channel = "cd-ui-production"
    } else {
        username = "${MattermostNames[userid]}"
    }

    if( username == "null" || username == null ) { username = "" } else { username = "@${username}"}
    def notify_msg = "**Attn:** ${username}\n"+
                    "**Repo:** ${repo}\n" +
                    "**Commit Hash:** ${commit_hash}\n" +
                    "**Result:** Promotion to ${environment} started. Please check link for details\n"+
                    "**Links: **[BuildURL](${RUN_DISPLAY_URL})\n"

    updateMattermost channel: "${channel}",
                color: "good",
                message: notify_msg
}

def send_notification(commit_hash,environment) {
    def color = "danger"
    def notify_id = ""
    def notify_msg = "**Attn:** "

    if(environment == "production"){
        channel = "cd-ui-production"
        notify_id = "${MattermostEmail[startedUserId]}"
    }else{
        def author = fetch_author(repo, commit_hash)
        notify_id = "${MattermostNames[author]}"
        echo "Author is ${author}"
    }

    if( notify_id == "null" || notify_id == null ) {
        notify_id = ""
    } else{
        notify_msg = notify_msg+"@"+notify_id
    }

    if( currentBuild.currentResult == "SUCCESS" ) {
        def fn_msg = "Promotion to ${environment} was successful. To promote to production use the promotion link below"
        if(environment == "production"){
            fn_msg = "Promotion to ${environment} was successful."
        }
        notify_msg = notify_msg+"\n" +
                    "**Repo:** ${repo}\n" +
                    "**Commit Hash:** ${commit_hash}\n" +
                    "**Result:** ${fn_msg}\n" +
                    "**Links:** [BuildURL](${RUN_DISPLAY_URL})"

        if(environment == "staging") {
            notify_msg = notify_msg +", [Promotion Link](https://jenkins.tools.netfoundry.io/blue/organizations/jenkins/admin-gui/branches)\n"
        }
        color = "good"
    } else {
        def fn_msg = "Promotion to ${environment} failed. Please check link for details."
        if(environment == "sandbox"){
            fn_msg = "Build Failed for branch. Check build url for details."
        }
	    notify_msg = notify_msg+"\n"+
                    "**Repo:** ${repo}\n" +
                    "**Commit Hash:** ${commit_hash}\n" +
                    "**Result:** ${fn_msg}\n"+
                    "**Failure Reason:** ${error_msg}\n"+
                    "**Links:** [BuildURL](${RUN_DISPLAY_URL})\n"     
    }
	updateMattermost channel: "${channel}",
              color: "${color}",
              message: notify_msg
}