import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.ConfigurationLoader
import com.sap.piper.k8s.ContainerMap
import groovy.transform.Field

import static com.sap.piper.Prerequisites.checkScript

@Field String STEP_NAME = 'piperStageWrapper'

void call(Map parameters = [:], body) {

    final script = checkScript(this, parameters) ?: this
    def utils = parameters.juStabUtils ?: new Utils()

    def stageName = parameters.stageName?:env.STAGE_NAME

    // load default & individual configuration
    Map config = ConfigurationHelper.newInstance(this)
        .loadStepDefaults()
        .mixin(ConfigurationLoader.defaultStageConfiguration(this, stageName))
        .mixinGeneralConfig(script.commonPipelineEnvironment)
        .mixinStageConfig(script.commonPipelineEnvironment, stageName)
        .mixin(parameters)
        .addIfEmpty('stageName', stageName)
        .dependingOn('stageName').mixin('ordinal')
        .use()

    stageLocking(config) {
        def containerMap = ContainerMap.instance.getMap().get(stageName) ?: [:]
        if (Boolean.valueOf(env.ON_K8S) && containerMap.size() > 0) {
            withEnv(["POD_NAME=${stageName}"]) {
                dockerExecuteOnKubernetes(script: script, containerMap: containerMap) {
                    executeStage(script, body, stageName, config, utils)
                }
            }
        } else {
            node(config.nodeLabel) {
                executeStage(script, body, stageName, config, utils)
            }
        }
    }
}

private void stageLocking(Map config, Closure body) {
    if (config.stageLocking) {
        lock(resource: "${env.JOB_NAME}/${config.ordinal}", inversePrecedence: true) {
            milestone config.ordinal
            body()
        }
    } else {
        body()
    }
}

private void executeStage(script, originalStage, stageName, config, utils) {
    boolean projectExtensions
    boolean globalExtensions
    def startTime = System.currentTimeMillis()

    try {
        //Add general stage stashes to config.stashContent
        config.stashContent += script.commonPipelineEnvironment.configuration.stageStashes?.get(stageName)?.unstash ?: []

        utils.unstashAll(config.stashContent)

        /* Defining the sources where to look for a project extension and a repository extension.
        * Files need to be named like the executed stage to be recognized.
        */
        def projectInterceptorFile = "${config.projectExtensionsDirectory}${stageName}.groovy"
        def globalInterceptorFile = "${config.globalExtensionsDirectory}${stageName}.groovy"
        projectExtensions = fileExists(projectInterceptorFile)
        globalExtensions = fileExists(globalInterceptorFile)
        // Pre-defining the real originalStage in body variable, might be overwritten later if extensions exist
        def body = originalStage

        // First, check if a global extension exists via a dedicated repository
        if (globalExtensions) {
            Script globalInterceptorScript = load(globalInterceptorFile)
            echo "[${STEP_NAME}] Found global interceptor '${globalInterceptorFile}' for ${stageName}."
            // If we call the global interceptor, we will pass on originalStage as parameter
            body = {
                globalInterceptorScript(script: script, originalStage: body, stageName: stageName, config: config)
            }
        }

        // Second, check if a project extension (within the same repository) exists
        if (projectExtensions) {
            Script projectInterceptorScript = load(projectInterceptorFile)
            echo "[${STEP_NAME}] Running project interceptor '${projectInterceptorFile}' for ${stageName}."
            // If we call the project interceptor, we will pass on body as parameter which contains either originalStage or the repository interceptor
            projectInterceptorScript(script: script, originalStage: body, stageName: stageName, config: config)
        } else {
            //TODO: assign projectInterceptorScript to body as done for globalInterceptorScript, currently test framework does not seem to support this case. Further investigations needed.
            body()
        }

    } finally {
        //Perform stashing of selected files in workspace
        utils.stashList(script, script.commonPipelineEnvironment.configuration.stageStashes?.get(stageName)?.stashes ?: [])
        deleteDir()

        def duration = System.currentTimeMillis() - startTime
        utils.pushToSWA([
            eventType: 'library-os-stage',
            stageName: stageName,
            stepParamKey1: 'buildResult',
            stepParam1: "${script.currentBuild.currentResult}",
            buildResult: "${script.currentBuild.currentResult}",
            stepParamKey2: 'stageStartTime',
            stepParam2: "${startTime}",
            stageStartTime: "${startTime}",
            stepParamKey3: 'stageDuration',
            stepParam3: "${duration}",
            stageDuration: "${duration}",
            stepParamKey4: 'projectExtension',
            stepParam4: "${projectExtensions}",
            projectExtension: "${projectExtensions}",
            stepParamKey5: 'globalExtension',
            stepParam5: "${globalExtensions}",
            globalExtension: "${globalExtensions}"
        ], config)
    }
}
