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

repo = "netfoundry-ui"
channel = "cd-ui-staging"
dep_environment = "sandbox"

GIT_HASH = ""

cypresstest_url = ""
cypress_reports_url = ""
currentDeployedCommit = ""
startedUserId = ""
failure_msg = ""
stage_name = ""
link_url = ""
dlink = ""

failure = false
set_cypresstest_url = false

BRANCH_NAME="develop"

def CF_DISTRIBUTION =  [ 'sandbox': 'E2DUG1N88KK0U5', 'staging': 'E34ZTY1TTL22H9', 'production': 'E2W1ONA5RY709N' ]


pipeline {
    agent {
        ecs {
            inheritFrom 'agent-nodejs'
            cpu 8192
            memoryReservation 16384
        }
    }

    environment {
        SONAR_CLOUD = credentials('sonar')
        ELASTICSEARCH = credentials('elasticsearch')
        BITBUCKET_AUTH = credentials('bitbucket_auth')
        BITBUCKET_ADMIN = credentials('bitbucket_admin')
        def uploadSpec = """{
            "files": [
                {
                "pattern": "netfoundry-ui.zip",
                "target": "netfoundry-ui",
                "props": "version=${env.BUILD_NUMBER}"
                }
            ]
        }"""
    }

    stages {
        stage('Prepare') {
            steps {
                script {
                    stage_name = env.STAGE_NAME

                    GIT_HASH = sh(returnStdout: true, script: "git rev-parse --short HEAD").trim()
                    echo "Hash is: ${GIT_HASH}"

                    bitbucketStatus repo: "${repo}", BRANCH_NAME: "${BRANCH_NAME}", GIT_COMMIT: "${GIT_HASH}", PR_CHECK: "false", status: "INPROGRESS"
                    author_email = fetch_author_email("${repo}", "${GIT_HASH}")

                    sh "df -k && ls -al /app/nx"
                    sh "pwd && whoami && node -v"
                    sh "rm -rf coverage dist node_modules"
                    sh "yarn config -v"
                    sh "yarn install --immutable"
                    sh '''sed -i -re 's/"cacheableOperations"/"cacheDirectory": "\\/app\\/nx", "cacheableOperations"/' nx.json'''
                    sh "cat nx.json"
                }
            }
        }

        stage('Build and Test') {
            parallel {
                stage('Test') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            sh "node node_modules/.bin/nx affected:test --base origin/develop --parallel"
                        }
                    }
                }

                stage('Lint & Build') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            sh "node node_modules/.bin/nx affected:lint --base origin/develop --parallel"
                            sh "node version.js"
                            sh "node node_modules/.bin/nx affected:build --base origin/master --configuration=sandbox --aot"
                            stash includes: 'dist/**/*', name: 'sandbox-dist'
                            sh "ls dist"
                        }
                    }
                }
            }
        }

       stage('Generate and Upload SBOM') {
           steps {
               script {
                   try {
                       generateAndUploadSBOM(repo)
                   } catch (Exception e) {
                       echo "SBOM generation failed: ${e.getMessage()}"
                   }
               }
           }
       }

        stage('Deployment to Sandbox') {
            steps {
                script {
                    stage_name = env.STAGE_NAME
                    dep_environment = "sandbox"
                    unstash 'sandbox-dist'
                    netfoundryUIDeploy("${dep_environment}", "${repo}", CF_DISTRIBUTION, "${GIT_HASH}")

                    sshagent(['new-jenkins-bitbucket-ssh']) {
                        runTerraform("${dep_environment}", "terraform", "plan")
                        runTerraform("${dep_environment}", "terraform", "apply")
                    }

                    //notification
                    fn_msg = "Merged changes are now deployed to ${dep_environment}, commit_hash: ${GIT_HASH}"
                    send_custom_notification("${author_email}", "${repo}", "${channel}", "${fn_msg}", "good", " ")
                }
            }
        }

        stage('Sanity Audit') {
            parallel {
                stage('Pre-Stage Cypress Tests') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            retry(1) {
                                try{
                                    build_job = build job: 'cypress_tests', parameters: [
                                        [$class: 'StringParameterValue', name: 'environment', value: "sandbox"]
                                    ], propagate: true, wait: true
                                } catch(e){
                                    String child_build_number = StringUtils.substringBetween("${e}", "#", "completed").trim();
                                    cypresstest_url = "https://jenkins-controller.tools.netfoundry.io/blue/organizations/jenkins/cyp_test/detail/cyp_test/${child_build_number}/pipeline"
                                    cypress_reports_url = "https://jenkins-controller.tools.netfoundry.io/job/cyp_test/${child_build_number}/artifact/cypress/reports/mochareports/report.html"
                                    echo "CypressTest URL is :- ${cypresstest_url}"
                                    echo "CypressReport URL is :- ${cypress_reports_url}"
                                    set_cypresstest_url = true
                                    failure_msg = "Pre-Stage Cypress Smoketest failed"
                                    error "${failure_msg}"
                                }
                            }
                        }
                    }
                }

                stage('Static Code Analysis') {
                    steps {
                        script {
                            stage_name = env.STAGE_NAME
                            catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                              unstash 'sandbox-dist'
                              //sonar cloud

                              sh "yarn install --immutable"
                              sh "./node_modules/sonar-scanner/bin/sonar-scanner -Dsonar.qualitygate.timeout=300 -Dsonar.organization=netfoundry -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=$SONAR_CLOUD_PSW"

                            }
                        }
                    }
                }
            }
        }

        stage('Promote to Staging') {
            steps {
                script {
                    stage_name = env.STAGE_NAME
                    dep_environment = "staging"
					currentBuild.description = "Promoting Build - Environment: ${dep_environment} REPO: ${repo} Hash: ${GIT_HASH}"

                    sh "rm -rf dist"
                    sh "yarn install --immutable"
                    sh "node --max_old_space_size=10000 node_modules/.bin/nx affected:build --base origin/master --configuration=staging --aot"
                    sh "ls dist"
                    netfoundryUIDeploy("${dep_environment}", "${repo}", CF_DISTRIBUTION, "${GIT_HASH}")

                    sshagent(['new-jenkins-bitbucket-ssh']) {
                        runTerraform("${dep_environment}", "terraform", "plan")
                        runTerraform("${dep_environment}", "terraform", "apply")
                    }

                    //notification
                    fn_msg = "Merged changes are now deployed to ${dep_environment}, commit_hash: ${GIT_HASH}"
                    send_custom_notification("${author_email}", "${repo}", "${channel}", "${fn_msg}", "good", " ")
                }
            }
        }

        stage('Cypress Tests') {
            when { expression { fileExists('dist/app/nfconsole') }}
            steps {
                script {
                    stage_name = env.STAGE_NAME
                    retry(1) {
                        try{
                            build_job = build job: 'cypress_tests', parameters: [
                                [$class: 'StringParameterValue', name: 'environment', value: "staging"]
                            ], propagate: true, wait: true
                        } catch(e){
                            String child_build_number = StringUtils.substringBetween("${e}", "#", "completed").trim();
                            cypresstest_url = "https://jenkins-controller.tools.netfoundry.io/blue/organizations/jenkins/cyp_test/detail/cyp_test/${child_build_number}/pipeline"
                            cypress_reports_url = "https://jenkins-controller.tools.netfoundry.io/job/cyp_test/${child_build_number}/artifact/cypress/reports/mochareports/report.html"
                            echo "CypressTest URL is :- ${cypresstest_url}"
                            echo "CypressReport URL is :- ${cypress_reports_url}"
                            set_cypresstest_url = true
                            failure_msg = "Post deployment Cypress Smoketest failed"
                            error "${failure_msg}"
                        }
                    }
                }
            }
        }

        stage('Create Release Candidates') {
            steps {
                script {
                    stage_name = env.STAGE_NAME
					sh "rm -rf coverage"
                    sh "rm -rf dist"
                    sh "rm -rf node_modules"
                    sh "yarn config -v"
                    sh "yarn install --immutable"
                    sh "node version.js"
                    sh "node --max_old_space_size=10000 node_modules/.bin/nx affected:build --base origin/master --configuration=production --aot"
                    sh "ls dist"

                    account_id = nfAWSAccounts account_name: "production"
                    withAWS(credentials: 'jenkins-central-mgt', region: 'us-east-1', role:'jenkins-central-mgt',  roleAccount: "${account_id}") {
                        if(fileExists('dist/apps/nfconsole')) {
                            archiveArtifacts "dist/apps/nfconsole/**"
                            sh "zip -r nfconsole-${GIT_HASH}.zip dist/apps/nfconsole"
                            sh "aws s3 cp nfconsole-${GIT_HASH}.zip s3://release-candidate-netfoundry-ui/"
                        }

                        if(fileExists('dist/apps/zeds')) {
                            sh "zip -r zeds-${GIT_HASH}.zip dist/apps/zeds"
                            sh "aws s3 cp zeds-${GIT_HASH}.zip s3://release-candidate-zeds-ui/"
                        }

                        if(fileExists('dist/apps/cloud-ziti')) {
                            sh "zip -r cloud-ziti-${GIT_HASH}.zip dist/apps/cloud-ziti"
                            sh "aws s3 cp cloud-ziti-${GIT_HASH}.zip s3://release-candidate-cloudziti-ui/"
                        }
                    }
                    currentBuild.description = "Release Candidates - Environment: ${dep_environment} \nZeds Artifact: zeds-${GIT_HASH}.zip \nCloudZiti Artifact: cloud-ziti-${GIT_HASH}.zip \nNFConsole Artifact: nfconsole-${GIT_HASH}.zip"
                }
            }
        }
    }

	post {
        always {
            script{
                sh "ls -A1 | xargs rm -r"
            }
            deleteDir() /* clean up our workspace --doesn't work currently, rm -rf added--*/
        }
        aborted {
            bitbucketStatus repo: "${repo}", BRANCH_NAME: BRANCH_NAME, GIT_COMMIT: GIT_HASH, PR_CHECK: "false", status: "STOPPED"
        }
        failure {
            script{
                bitbucketStatus repo: "${repo}", BRANCH_NAME: BRANCH_NAME, GIT_COMMIT: GIT_HASH, PR_CHECK: "false", status: "FAILED"
                if(set_cypresstest_url == true) {
                    link_url = link_url+", [Cypress Test Build](${cypresstest_url})"
                    link_url = link_url+", [Cypress Report](${cypress_reports_url})"
                }
                currentDeployedCommit = read_ssm("${repo}", "${dep_environment}")
                dlink = "https://bitbucket.org/netfoundry/${repo}/branches/compare/${GIT_HASH}..${currentDeployedCommit}#pull-requests"
                if(currentDeployedCommit != GIT_HASH) {
                    link_url = link_url+", [Git Diff Link](${dlink})"
                }
                mattermostFailureNotification repo: "${repo}", commit: GIT_HASH, build_url: RUN_DISPLAY_URL, channel: "${channel}", link_url: "${link_url}", reason: "${stage_name} stage failed. env: ${dep_environment}. ${failure_msg}. Please check link for details."

                //Capture log to ElasticSearch
                failure = true
                if("${dep_environment}" != "sandbox"){
                    try{
                        log_commit_elasticsearch("${dep_environment}", "${repo}", "${GIT_HASH}", "${startedUserId}", failure)
                    } catch(e) {
                        echo "Capture log to ElasticSearch failed. Error: ${e}"
                    }
                }
            }
        }
        success {
            script{
                bitbucketStatus repo: "${repo}", BRANCH_NAME: BRANCH_NAME, GIT_COMMIT: GIT_HASH, PR_CHECK: "false", status: "SUCCESSFUL"
                //Capture log to ElasticSearch
                if("${dep_environment}" != "sandbox"){
                    try{
                        log_commit_elasticsearch("${dep_environment}", "${repo}", "${GIT_HASH}", "${startedUserId}", failure)
                    } catch(e) {
                        echo "Capture log to ElasticSearch failed. Error: ${e}"
                    }
                }
            }
        }
    }
}
