import static com.sap.piper.Prerequisites.checkScript

import com.cloudbees.groovy.cps.NonCPS

import com.sap.piper.GenerateDocumentation
import com.sap.piper.ConfigurationHelper
import com.sap.piper.JenkinsUtils
import com.sap.piper.MapUtils
import com.sap.piper.Utils
import groovy.transform.Field

@Field List TOOLS = [
    /**
     * Publishes test results files in JUnit format with the [JUnit Plugin](https://plugins.jenkins.io/junit).
     * @possibleValues `true`, `false`, `Map`
     */
    'junit',
    /**
     * Publishes code coverage with the [JaCoCo plugin](https://plugins.jenkins.io/jacoco).
     * @possibleValues `true`, `false`, `Map`
     */
    'jacoco',
    /**
     * Publishes code coverage with the [Cobertura plugin](https://plugins.jenkins.io/cobertura).
     * @possibleValues `true`, `false`, `Map`
     */
    'cobertura',
    /**
     * Publishes performance test results with the [Performance plugin](https://plugins.jenkins.io/performance).
     * @possibleValues `true`, `false`, `Map`
     */
    'jmeter'
]

@Field def STEP_NAME = getClass().getName()

@Field Set GENERAL_CONFIG_KEYS = TOOLS

@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
    /**
     * If it is set to `true` the step will fail the build if JUnit detected any failing tests.
     * @possibleValues `true`, `false`
     */
    'failOnError'
])

@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS

/**
 * This step can publish test results from various sources.
 */
@GenerateDocumentation
void call(Map parameters = [:]) {
    handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
        def script = checkScript(this, parameters) ?: this

        prepare(parameters)

        // load default & individual configuration
        Map configuration = ConfigurationHelper.newInstance(this)
            .loadStepDefaults()
            .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
            .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
            .mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS)
            .mixin(parameters, PARAMETER_KEYS)
            .use()

        new Utils().pushToSWA([
            step: STEP_NAME,
            stepParamKey1: 'scriptMissing',
            stepParam1: parameters?.script == null
        ], configuration)

        publishJUnitReport(configuration.get('junit'))
        publishJacocoReport(configuration.get('jacoco'))
        publishCoberturaReport(configuration.get('cobertura'))
        publishJMeterReport(configuration.get('jmeter'))

        if (configuration.failOnError && JenkinsUtils.hasTestFailures(script.currentBuild)) {
            script.currentBuild.result = 'FAILURE'
            error "[${STEP_NAME}] Some tests failed!"
        }
    }
}

def publishJUnitReport(Map settings = [:]) {
    if(settings.active){
        def pattern = settings.get('pattern')
        def allowEmpty = settings.get('allowEmptyResults')

        if (settings.get('updateResults'))
            touchFiles(pattern)
        junit(
            testResults: pattern,
            allowEmptyResults: allowEmpty,
            healthScaleFactor: 100.0,
        )
        archiveResults(settings.get('archive'), pattern, allowEmpty)
    }
}

def publishJacocoReport(Map settings = [:]) {
    if(settings.active){
        def pattern = settings.get('pattern')
        def allowEmpty = settings.get('allowEmptyResults')

        jacoco(
            execPattern: pattern,
            inclusionPattern: settings.get('include', ''),
            exclusionPattern: settings.get('exclude', '')
        )
        archiveResults(settings.get('archive'), pattern, allowEmpty)
    }
}

def publishCoberturaReport(Map settings = [:]) {
    if(settings.active){
        def pattern = settings.get('pattern')
        def allowEmpty = settings.get('allowEmptyResults')

        cobertura(
            coberturaReportFile: pattern,
            onlyStable: settings.get('onlyStableBuilds'),
            failNoReports: !allowEmpty,
            failUnstable: false,
            failUnhealthy: false,
            autoUpdateHealth: false,
            autoUpdateStability: false,
            maxNumberOfBuilds: 0
        )
        archiveResults(settings.get('archive'), pattern, allowEmpty)
    }
}

// publish Performance Report using "Jenkins Performance Plugin" https://wiki.jenkins.io/display/JENKINS/Performance+Plugin
def publishJMeterReport(Map settings = [:]){
    if(settings.active){
        def pattern = settings.get('pattern')

        perfReport(
            sourceDataFiles: pattern,
            errorFailedThreshold: settings.get('errorFailedThreshold'),
            errorUnstableThreshold: settings.get('errorUnstableThreshold'),
            errorUnstableResponseTimeThreshold: settings.get('errorUnstableResponseTimeThreshold'),
            relativeFailedThresholdPositive: settings.get('relativeFailedThresholdPositive'),
            relativeFailedThresholdNegative: settings.get('relativeFailedThresholdNegative'),
            relativeUnstableThresholdPositive: settings.get('relativeUnstableThresholdPositive'),
            relativeUnstableThresholdNegative: settings.get('relativeUnstableThresholdNegative'),
            modePerformancePerTestCase: false,
            modeOfThreshold: settings.get('modeOfThreshold'),
            modeThroughput: settings.get('modeThroughput'),
            nthBuildNumber: settings.get('nthBuildNumber'),
            configType: settings.get('configType'),
            failBuildIfNoResultFile: settings.get('failBuildIfNoResultFile'),
            compareBuildPrevious: settings.get('compareBuildPrevious')
        )
        archiveResults(settings.get('archive'), pattern, settings.get('allowEmptyResults'))
    }
}

void touchFiles(pattern){
    echo "[${STEP_NAME}] update test results"
    def patternArray = pattern.split(',')
    for(def i = 0; i < patternArray.length; i++){
        sh "find . -wholename '${patternArray[i].trim()}' -exec touch {} \\;"
    }
}

def archiveResults(archive, pattern, allowEmpty) {
    if(archive){
        echo "[${STEP_NAME}] archive ${pattern}"
        archiveArtifacts artifacts: pattern, allowEmptyArchive: allowEmpty
    }
}

@NonCPS
def prepare(parameters){
    // ensure tool maps are initialized correctly
    for(String tool : TOOLS){
        parameters[tool] = toMap(parameters[tool])
    }
    return parameters
}

@NonCPS
def toMap(parameters){
    if(MapUtils.isMap(parameters))
        parameters.put('active', parameters.active == null?true:parameters.active)
    else if(Boolean.TRUE.equals(parameters))
        parameters = [active: true]
    else if(Boolean.FALSE.equals(parameters))
        parameters = [active: false]
    else
        parameters = [:]
    return parameters
}
