import com.sap.piper.ConfigurationHelper
import com.sap.piper.GenerateDocumentation
import com.sap.piper.Utils

import static com.sap.piper.Prerequisites.checkScript

import groovy.transform.Field
import groovy.text.SimpleTemplateEngine

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

@Field Set GENERAL_CONFIG_KEYS = [
    /**
     * Pull-Request voting only:
     * The URL to the Github API. see https://docs.sonarqube.org/display/PLUG/GitHub+Plugin#GitHubPlugin-Usage
     * deprecated: only supported in LTS / < 7.2
     */
    'githubApiUrl',
    /**
     * Pull-Request voting only:
     * The Github organization.
     * @default: `commonPipelineEnvironment.getGithubOrg()`
     */
    'githubOrg',
    /**
     * Pull-Request voting only:
     * The Github repository.
     * @default: `commonPipelineEnvironment.getGithubRepo()`
     */
    'githubRepo',
    /**
     * Pull-Request voting only:
     * The Jenkins credentialId for a Github token. It is needed to report findings back to the pull-request.
     * deprecated: only supported in LTS / < 7.2
     * @possibleValues Jenkins credential id
     */
    'githubTokenCredentialsId',
    /**
     * The Jenkins credentialsId for a SonarQube token. It is needed for non-anonymous analysis runs. see https://sonarcloud.io/account/security
     * @possibleValues Jenkins credential id
     */
    'sonarTokenCredentialsId',
]
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
    /**
     * Pull-Request voting only:
     * Disables the pull-request decoration with inline comments.
     * deprecated: only supported in LTS / < 7.2
     * @possibleValues `true`, `false`
     */
    'disableInlineComments',
    /**
     * Name of the docker image that should be used. If empty, Docker is not used and the command is executed directly on the Jenkins system.
     * see dockerExecute
     */
    'dockerImage',
    /**
     * The name of the SonarQube instance defined in the Jenkins settings.
     */
    'instance',
    /**
     * Pull-Request voting only:
     * Activates the pull-request handling using the [GitHub Plugin](https://docs.sonarqube.org/display/PLUG/GitHub+Plugin) (deprecated).
     * deprecated: only supported in LTS / < 7.2
     * @possibleValues `true`, `false`
     */
    'legacyPRHandling',
    /**
     * A list of options which are passed to the `sonar-scanner`.
     */
    'options',
    /**
     * Organization that the project will be assigned to in SonarCloud.io.
     */
    'organization',
    /**
     * The project version that is reported to SonarQube.
     * @default: major number of `commonPipelineEnvironment.getArtifactVersion()`
     */
    'projectVersion'
])
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS

/**
 * The step executes the [sonar-scanner](https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner) cli command to scan the defined sources and publish the results to a SonarQube instance.
 */
@GenerateDocumentation
void call(Map parameters = [:]) {
    handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
        def utils = parameters.juStabUtils ?: new Utils()
        def script = checkScript(this, parameters) ?: this
        // 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, GENERAL_CONFIG_KEYS)
            .mixin(parameters, PARAMETER_KEYS)
            .addIfEmpty('projectVersion', script.commonPipelineEnvironment.getArtifactVersion()?.tokenize('.')?.get(0))
            .addIfEmpty('githubOrg', script.commonPipelineEnvironment.getGithubOrg())
            .addIfEmpty('githubRepo', script.commonPipelineEnvironment.getGithubRepo())
            // check mandatory parameters
            .withMandatoryProperty('githubTokenCredentialsId', null, { config -> config.legacyPRHandling && isPullRequest() })
            .withMandatoryProperty('githubOrg', null, { isPullRequest() })
            .withMandatoryProperty('githubRepo', null, { isPullRequest() })
            .use()

        if(configuration.options instanceof String)
            configuration.options = [].plus(configuration.options)

        def worker = { config ->
            withSonarQubeEnv(config.instance) {
                loadSonarScanner(config)

                if(config.organization) config.options.add("sonar.organization=${config.organization}")
                if(config.projectVersion) config.options.add("sonar.projectVersion=${config.projectVersion}")
                // prefix options
                config.options = config.options.collect { it.startsWith('-D') ? it : "-D${it}" }

                sh "PATH=\$PATH:${env.WORKSPACE}/.sonar-scanner/bin sonar-scanner ${config.options.join(' ')}"
            }
        }

        if(configuration.sonarTokenCredentialsId){
            def workerForSonarAuth = worker
            worker = { config ->
                withCredentials([string(
                    credentialsId: config.sonarTokenCredentialsId,
                    variable: 'SONAR_TOKEN'
                )]){
                    config.options.add("sonar.login=$SONAR_TOKEN")
                    workerForSonarAuth(config)
                }
            }
        }

        if(isPullRequest()){
            def workerForGithubAuth = worker
            worker = { config ->
                if(config.legacyPRHandling) {
                    withCredentials([string(
                        credentialsId: config.githubTokenCredentialsId,
                        variable: 'GITHUB_TOKEN'
                    )]){
                        // support for https://docs.sonarqube.org/display/PLUG/GitHub+Plugin
                        config.options.add('sonar.analysis.mode=preview')
                        config.options.add("sonar.github.oauth=$GITHUB_TOKEN")
                        config.options.add("sonar.github.pullRequest=${env.CHANGE_ID}")
                        config.options.add("sonar.github.repository=${config.githubOrg}/${config.githubRepo}")
                        if(config.githubApiUrl) config.options.add("sonar.github.endpoint=${config.githubApiUrl}")
                        if(config.disableInlineComments) config.options.add("sonar.github.disableInlineComments=${config.disableInlineComments}")
                        workerForGithubAuth(config)
                    }
                } else {
                    // see https://sonarcloud.io/documentation/analysis/pull-request/
                    config.options.add("sonar.pullrequest.key=${env.CHANGE_ID}")
                    config.options.add("sonar.pullrequest.base=${env.CHANGE_TARGET}")
                    config.options.add("sonar.pullrequest.branch=${env.BRANCH_NAME}")
                    config.options.add("sonar.pullrequest.provider=${config.pullRequestProvider}")
                    switch(config.pullRequestProvider){
                        case 'github':
                            config.options.add("sonar.pullrequest.github.repository=${config.githubOrg}/${config.githubRepo}")
                            break
                        default: error "Pull-Request provider '${config.pullRequestProvider}' is not supported!"
                    }
                    workerForGithubAuth(config)
                }
            }
        }

        dockerExecute(
            script: script,
            dockerImage: configuration.dockerImage
        ){
            worker(configuration)
        }
    }
}

private Boolean isPullRequest(){
    return env.CHANGE_ID
}

private void loadSonarScanner(config){
    def filename = new File(config.sonarScannerDownloadUrl).getName()
    def foldername = filename.replace('.zip', '').replace('cli-', '')

    sh """
        curl --remote-name --remote-header-name --location --silent --show-error ${config.sonarScannerDownloadUrl}
        unzip -q ${filename}
        mv ${foldername} .sonar-scanner
    """
}
