pipeline {
    agent {
        label 'pyDABFActory-CIAgent'
    }

    parameters {
        booleanParam(name: 'BUILD_ALL_PROFILES', defaultValue: true, description: 'Build all profiles one by one')
        string(name: 'PROFILE', defaultValue: 'demo', description: 'Profile to build when BUILD_ALL_PROFILES is false')
    }

    environment {
        PYTHON_DOCKERFILE = 'builder/docker/builder-alpine/Dockerfile'
        ETCH_DOCKERFILE   = 'builder/docker/packages-builder-etch/Dockerfile'
        LENNY_DOCKERFILE  = 'builder/docker/live-helper/Dockerfile'
    }

    stages {
        stage('Build runtimes') {
            steps {
                script {
                    env.RUNTIME_BUILDER_IMAGE = docker.build(
                        "retrodebian/builder-alpine:${env.BUILD_TAG}",
                        "-f ${env.PYTHON_DOCKERFILE} ."
                    ).imageName()

                    env.RUNTIME_ETCH_IMAGE = docker.build(
                        "retrodebian/package-builder-etch:${env.BUILD_TAG}",
                        "-f ${env.ETCH_DOCKERFILE} ."
                    ).imageName()

                    env.RUNTIME_LENNY_IMAGE = docker.build(
                        "retrodebian/live-helper:${env.BUILD_TAG}",
                        "-f ${env.LENNY_DOCKERFILE} ."
                    ).imageName()
                }
            }
        }

        stage('Validate builder') {
            steps {
                script {
                    docker.image(env.RUNTIME_BUILDER_IMAGE).inside {
                        sh 'python3 builder/py/build.py validate'
                    }
                }
            }
        }

        stage('Build common features and profiles') {
            steps {
                script {
                    def jsonSlurper = new groovy.json.JsonSlurperClassic()
                    def dockerOverrideCache = [:]
                    def profileResolveCache = [:]

                    def withImage = { String imageName, Closure body ->
                        docker.image(imageName).inside {
                            body()
                        }
                    }

                    def withBuilder = { Closure body ->
                        withImage(env.RUNTIME_BUILDER_IMAGE, body)
                    }

                    def builderJson = { String command ->
                        def raw = ''
                        withBuilder {
                            raw = sh(script: command, returnStdout: true).trim()
                        }
                        return raw ? jsonSlurper.parseText(raw) : [:]
                    }

                    def resolveModuleRuntimeImage = { String runtimeName, String kind, String name, String phase, String defaultImage ->
                        def cacheKey = "${runtimeName}:${kind}:${name}:${phase}"
                        if (dockerOverrideCache.containsKey(cacheKey)) {
                            return dockerOverrideCache[cacheKey]
                        }

                        def payload = builderJson("python3 builder/py/build.py module-docker --kind ${kind} --name ${name} --phase ${phase} --json")
                        def overrideImage = (payload.image ?: '').toString().trim()
                        def overrideDockerfile = (payload.dockerfile ?: '').toString().trim()
                        def overrideContext = (payload.docker_context ?: '').toString().trim()

                        def resolved = defaultImage
                        if (overrideDockerfile) {
                            def tag = overrideImage ? overrideImage : "retrodebian/${runtimeName}-${kind}-${name}-${phase}:${env.BUILD_TAG}"
                            def contextPath = overrideContext ? overrideContext : '.'
                            resolved = docker.build(tag, "-f ${overrideDockerfile} ${contextPath}").imageName()
                        } else if (overrideImage) {
                            resolved = overrideImage
                        }

                        dockerOverrideCache[cacheKey] = resolved
                        return resolved
                    }

                    def resolveProfile = { String profileName ->
                        if (profileResolveCache.containsKey(profileName)) {
                            return profileResolveCache[profileName]
                        }
                        def payload = builderJson("python3 builder/py/build.py profile-resolve --profile ${profileName}")
                        profileResolveCache[profileName] = payload
                        return payload
                    }

                    def featureNames = []
                    withBuilder {
                        def raw = sh(script: 'python3 builder/py/build.py list features', returnStdout: true).trim()
                        featureNames = raw ? raw.split('\n').findAll { it?.trim() } : []
                    }

                    for (featureName in featureNames) {
                        stage("Feature ${featureName}") {
                            def preGenPythonImage = resolveModuleRuntimeImage('python', 'feature', featureName, 'pre-gen', env.RUNTIME_BUILDER_IMAGE)
                            def generateEtchImage = resolveModuleRuntimeImage('etch', 'feature', featureName, 'generate', env.RUNTIME_ETCH_IMAGE)
                            def postGenPythonImage = resolveModuleRuntimeImage('python', 'feature', featureName, 'post-gen', env.RUNTIME_BUILDER_IMAGE)

                            withImage(preGenPythonImage) {
                                sh "python3 builder/py/build.py run-python-phase --kind feature --phase pre-gen --profile ${params.PROFILE} --feature ${featureName}"
                                sh "python3 builder/py/build.py export-env --kind feature --phase pre-gen --profile ${params.PROFILE} --feature ${featureName} --output artifacts/features/${featureName}/runtime.env"
                            }

                            withImage(generateEtchImage) {
                                sh "builder/bash/run_generate.sh features/${featureName} artifacts/features/${featureName}/runtime.env"
                            }

                            withImage(postGenPythonImage) {
                                sh "python3 builder/py/build.py run-python-phase --kind feature --phase post-gen --profile ${params.PROFILE} --feature ${featureName}"
                            }
                        }
                    }

                    def profileNames = []
                    withBuilder {
                        if (params.BUILD_ALL_PROFILES) {
                            def raw = sh(script: 'python3 builder/py/build.py list profiles', returnStdout: true).trim()
                            profileNames = raw ? raw.split('\n').findAll { it?.trim() } : []
                        } else {
                            profileNames = [params.PROFILE]
                        }
                    }

                    for (profileName in profileNames) {
                        stage("Profile ${profileName}") {
                            def profileData = resolveProfile(profileName)
                            def baseName = profileData.base.toString()
                            def baseChain = (profileData.base_chain ?: []) as List
                            def featureList = (profileData.features ?: []) as List

                            for (baseItem in baseChain) {
                                stage("Base ${profileName} / ${baseItem}") {
                                    def preGenPythonImage = resolveModuleRuntimeImage('python', 'base', baseItem, 'pre-gen', env.RUNTIME_BUILDER_IMAGE)
                                    def generateEtchImage = resolveModuleRuntimeImage('etch', 'base', baseItem, 'generate', env.RUNTIME_ETCH_IMAGE)
                                    def postGenPythonImage = resolveModuleRuntimeImage('python', 'base', baseItem, 'post-gen', env.RUNTIME_BUILDER_IMAGE)

                                    withImage(preGenPythonImage) {
                                        sh "python3 builder/py/build.py run-python-phase --kind base --phase pre-gen --profile ${profileName} --base ${baseItem}"
                                        sh "python3 builder/py/build.py export-env --kind base --phase pre-gen --profile ${profileName} --base ${baseItem} --output artifacts/bases/${baseItem}/runtime.env"
                                    }

                                    withImage(generateEtchImage) {
                                        sh "builder/bash/run_generate.sh bases/${baseItem} artifacts/bases/${baseItem}/runtime.env"
                                    }

                                    withImage(postGenPythonImage) {
                                        sh "python3 builder/py/build.py run-python-phase --kind base --phase post-gen --profile ${profileName} --base ${baseItem}"
                                    }
                                }
                            }

                            withBuilder {
                                sh "python3 builder/py/build.py prepare-profile --profile ${profileName}"
                                sh "python3 builder/py/build.py export-env --kind profile --phase config --profile ${profileName} --output artifacts/profiles/${profileName}/profile-config.env"
                                sh "python3 builder/py/build.py inject-resources --kind base --name ${baseName}"
                            }

                            withImage(env.RUNTIME_LENNY_IMAGE) {
                                sh "builder/bash/run_profile_config.sh artifacts/profiles/${profileName}/profile-config.env"
                            }

                            def basePreFeaturePythonImage = resolveModuleRuntimeImage('python', 'base', baseName, 'pre-feature', env.RUNTIME_BUILDER_IMAGE)
                            def basePreFeatureLennyImage = resolveModuleRuntimeImage('lenny', 'base', baseName, 'pre-feature', env.RUNTIME_LENNY_IMAGE)

                            withImage(basePreFeaturePythonImage) {
                                sh "python3 builder/py/build.py run-python-phase --kind base --phase pre-feature --profile ${profileName} --base ${baseName}"
                                sh "python3 builder/py/build.py export-env --kind base --phase pre-feature --profile ${profileName} --base ${baseName} --output artifacts/bases/${baseName}/pre-feature.env"
                            }

                            withImage(basePreFeatureLennyImage) {
                                sh "builder/bash/run_entry.sh bases/${baseName} artifacts/bases/${baseName}/pre-feature.env pre-feature"
                            }

                            for (featureName in featureList) {
                                stage("Inject ${profileName} / ${featureName}") {
                                    def featurePreInjPythonImage = resolveModuleRuntimeImage('python', 'feature', featureName, 'pre-inj', env.RUNTIME_BUILDER_IMAGE)
                                    def featurePreInjLennyImage = resolveModuleRuntimeImage('lenny', 'feature', featureName, 'pre-inj', env.RUNTIME_LENNY_IMAGE)
                                    def featurePostInjLennyImage = resolveModuleRuntimeImage('lenny', 'feature', featureName, 'post-inj', env.RUNTIME_LENNY_IMAGE)
                                    def featurePostInjPythonImage = resolveModuleRuntimeImage('python', 'feature', featureName, 'post-inj', env.RUNTIME_BUILDER_IMAGE)

                                    withBuilder {
                                        sh "python3 builder/py/build.py save-feature-metadata --profile ${profileName} --feature ${featureName}"
                                    }

                                    withImage(featurePreInjPythonImage) {
                                        sh "python3 builder/py/build.py run-python-phase --kind feature --phase pre-inj --profile ${profileName} --feature ${featureName}"
                                        sh "python3 builder/py/build.py export-env --kind feature --phase pre-inj --profile ${profileName} --feature ${featureName} --output artifacts/features/${featureName}/pre-inj.env"
                                    }

                                    withImage(featurePreInjLennyImage) {
                                        sh "builder/bash/run_entry.sh features/${featureName} artifacts/features/${featureName}/pre-inj.env pre-inj"
                                    }

                                    withBuilder {
                                        sh "python3 builder/py/build.py inject-resources --kind feature --name ${featureName}"
                                        sh "python3 builder/py/build.py export-env --kind feature --phase post-inj --profile ${profileName} --feature ${featureName} --output artifacts/features/${featureName}/post-inj.env"
                                    }

                                    withImage(featurePostInjLennyImage) {
                                        sh "builder/bash/run_entry.sh features/${featureName} artifacts/features/${featureName}/post-inj.env post-inj"
                                    }

                                    withImage(featurePostInjPythonImage) {
                                        sh "python3 builder/py/build.py run-python-phase --kind feature --phase post-inj --profile ${profileName} --feature ${featureName}"
                                    }
                                }
                            }

                            def basePostFeaturePythonImage = resolveModuleRuntimeImage('python', 'base', baseName, 'post-feature', env.RUNTIME_BUILDER_IMAGE)
                            def basePostFeatureLennyImage = resolveModuleRuntimeImage('lenny', 'base', baseName, 'post-feature', env.RUNTIME_LENNY_IMAGE)

                            withImage(basePostFeaturePythonImage) {
                                sh "python3 builder/py/build.py run-python-phase --kind base --phase post-feature --profile ${profileName} --base ${baseName}"
                                sh "python3 builder/py/build.py export-env --kind base --phase post-feature --profile ${profileName} --base ${baseName} --output artifacts/bases/${baseName}/post-feature.env"
                                sh "python3 builder/py/build.py profile-pre-build --profile ${profileName}"
                                sh "python3 builder/py/build.py export-env --kind profile --phase build --profile ${profileName} --output artifacts/profiles/${profileName}/profile-build.env"
                            }

                            withImage(basePostFeatureLennyImage) {
                                sh "builder/bash/run_entry.sh bases/${baseName} artifacts/bases/${baseName}/post-feature.env post-feature"
                            }

                            withImage(env.RUNTIME_LENNY_IMAGE) {
                                sh "builder/bash/run_profile_build.sh artifacts/profiles/${profileName}/profile-build.env"
                            }

                            withBuilder {
                                sh "python3 builder/py/build.py profile-finalize --profile ${profileName}"
                            }
                        }
                    }
                }
            }
        }
    }

    post {
        always {
            archiveArtifacts artifacts: 'artifacts/**/*', allowEmptyArchive: true
            archiveArtifacts artifacts: 'live/**/*', allowEmptyArchive: true
        }
    }
}
