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.MapUtils
import com.sap.piper.Utils

import groovy.transform.Field

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

@Field Set TOOLS = [
    /**
     * Allows to publish the check results.
     * @possibleValues `true`, `false`, `Map`
     */
    'aggregation',
    /**
     * Searches and publishes TODOs in files with the [Task Scanner Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Task+Scanner+Plugin).
     * @possibleValues `true`, `false`, `Map`
     */
    'tasks',
    /**
     * Publishes PMD findings with the [PMD plugin](https://plugins.jenkins.io/pmd).
     * @possibleValues `true`, `false`, `Map`
     */
    'pmd',
    /**
     * Publishes CPD findings with the [DRY plugin](https://plugins.jenkins.io/dry).
     * @possibleValues `true`, `false`, `Map`
     */
    'cpd',
    /**
     * Publishes Findbugs findings with the [Findbugs plugin](https://plugins.jenkins.io/findbugs).
     * @possibleValues `true`, `false`, `Map`
     */
    'findbugs',
    /**
     * Publishes Checkstyle findings with the [Checkstyle plugin](https://plugins.jenkins.io/checkstyle).
     * @possibleValues `true`, `false`, `Map`
     */
    'checkstyle',
    /**
     * Publishes ESLint findings (in [JSLint format](https://eslint.org/docs/user-guide/formatters/)) with the [Warnings plugin](https://plugins.jenkins.io/warnings).
     * @possibleValues `true`, `false`, `Map`
     */
    'eslint',
    /**
     * Publishes PyLint findings with the [Warnings plugin](https://plugins.jenkins.io/warnings), pylint needs to run with `--output-format=parseable` option.
     * @possibleValues `true`, `false`, `Map`
     */
    'pylint'
]

@Field Set GENERAL_CONFIG_KEYS = []
@Field Set STEP_CONFIG_KEYS = TOOLS.plus(['archive'])
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS

/**
 * This step can publish static check results from various sources.
 */
@GenerateDocumentation
void call(Map parameters = [:]) {
    handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {

        def script = checkScript(this, parameters)
        if (script == null)
            script = 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)

        // JAVA
        report('PmdPublisher', configuration.pmd, configuration.archive)
        report('DryPublisher', configuration.cpd, configuration.archive)
        report('FindBugsPublisher', configuration.findbugs, configuration.archive)
        report('CheckStylePublisher', configuration.checkstyle, configuration.archive)
        // JAVA SCRIPT
        reportWarnings('JSLint', configuration.eslint, configuration.archive)
        // PYTHON
        reportWarnings('PyLint', configuration.pylint, configuration.archive)
        // GENERAL
        reportTasks(configuration.tasks)
        aggregateReports(configuration.aggregation)
    }
}

def aggregateReports(settings){
    if (settings.active) {
        def options = createCommonOptionsMap('AnalysisPublisher', settings)
        // publish
        step(options)
    }
}

def reportTasks(settings){
    if (settings.active) {
        def options = createCommonOptionsMap('TasksPublisher', settings)
        options.put('pattern', settings.get('pattern'))
        options.put('high', settings.get('high'))
        options.put('normal', settings.get('normal'))
        options.put('low', settings.get('low'))
        // publish
        step(options)
    }
}

def report(publisherName, settings, doArchive){
    if (settings.active) {
        def pattern = settings.get('pattern')
        def options = createCommonOptionsMap(publisherName, settings)
        options.put('pattern', pattern)
        // publish
        step(options)
        // archive check results
        archiveResults(doArchive && settings.get('archive'), pattern, true)
    }
}

def reportWarnings(parserName, settings, doArchive){
    if (settings.active) {
        def pattern = settings.get('pattern')
        def options = createCommonOptionsMap('WarningsPublisher', settings)
        options.put('parserConfigurations', [[
            parserName: parserName,
            pattern: pattern
        ]])
        // publish
        step(options)
        // archive check results
        archiveResults(doArchive && settings.get('archive'), pattern, true)
    }
}

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

@NonCPS
def createCommonOptionsMap(publisherName, settings){
    Map result = [:]
    def thresholds = settings.get('thresholds', [:])
    def fail = thresholds.get('fail', [:])
    def unstable = thresholds.get('unstable', [:])

    result.put('$class', publisherName)
    result.put('healthy', settings.get('healthy'))
    result.put('unHealthy', settings.get('unHealthy'))
    result.put('canRunOnFailed', true)
    result.put('failedTotalAll', fail.get('all'))
    result.put('failedTotalHigh', fail.get('high'))
    result.put('failedTotalNormal', fail.get('normal'))
    result.put('failedTotalLow', fail.get('low'))
    result.put('unstableTotalAll', unstable.get('all'))
    result.put('unstableTotalHigh', unstable.get('high'))
    result.put('unstableTotalNormal', unstable.get('normal'))
    result.put('unstableTotalLow', unstable.get('low'))
    // filter empty values
    result = result.findAll {
        return it.value != null && it.value != ''
    }
    return result
}

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

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