import org.gradle.util.GradleVersion

// note that there is no mechanism to share code between the initscript{} block and the main script, so some logic is duplicated

// conditionally apply the GE / Build Scan plugin to the classpath so it can be applied to the build further down in this script
initscript {
    def isTopLevelBuild = !gradle.parent
    if (!isTopLevelBuild) {
        return
    }

    def getInputParam = { String name ->
        def envVarName = name.toUpperCase().replace('.', '_').replace('-', '_')
        return System.getProperty(name) ?: System.getenv(envVarName)
    }

    // finish early if injection is disabled
    def gradleInjectionEnabled = getInputParam("gradle-enterprise.injection-enabled")
    if (gradleInjectionEnabled != "true") {
        return
    }

    def pluginRepositoryUrl = getInputParam('gradle-enterprise.plugin-repository.url')
    def gePluginVersion = getInputParam('gradle-enterprise.plugin.version')
    def ccudPluginVersion = getInputParam('gradle-enterprise.ccud-plugin.version')

    def atLeastGradle5 = GradleVersion.current() >= GradleVersion.version('5.0')
    def atLeastGradle4 = GradleVersion.current() >= GradleVersion.version('4.0')

    if (gePluginVersion || ccudPluginVersion && atLeastGradle4) {
        pluginRepositoryUrl = pluginRepositoryUrl ?: 'https://plugins.gradle.org/m2'
        logger.quiet("Gradle Enterprise plugins resolution: $pluginRepositoryUrl")

        repositories {
            maven { url pluginRepositoryUrl }
        }
    }

    dependencies {
        if (gePluginVersion) {
            classpath atLeastGradle5 ?
                "com.gradle:gradle-enterprise-gradle-plugin:$gePluginVersion" :
                "com.gradle:build-scan-plugin:1.16"
        }

        if (ccudPluginVersion && atLeastGradle4) {
            classpath "com.gradle:common-custom-user-data-gradle-plugin:$ccudPluginVersion"
        }
    }
}

def BUILD_SCAN_PLUGIN_ID = 'com.gradle.build-scan'
def BUILD_SCAN_PLUGIN_CLASS = 'com.gradle.scan.plugin.BuildScanPlugin'

def GRADLE_ENTERPRISE_PLUGIN_ID = 'com.gradle.enterprise'
def GRADLE_ENTERPRISE_PLUGIN_CLASS = 'com.gradle.enterprise.gradleplugin.GradleEnterprisePlugin'
def GRADLE_ENTERPRISE_EXTENSION_CLASS = 'com.gradle.enterprise.gradleplugin.GradleEnterpriseExtension'
def CI_AUTO_INJECTION_CUSTOM_VALUE_NAME = 'CI auto injection'
def CI_AUTO_INJECTION_CUSTOM_VALUE_VALUE = 'gradle-build-action'
def CCUD_PLUGIN_ID = 'com.gradle.common-custom-user-data-gradle-plugin'
def CCUD_PLUGIN_CLASS = 'com.gradle.CommonCustomUserDataGradlePlugin'

def isTopLevelBuild = !gradle.parent
if (!isTopLevelBuild) {
    return
}

def getInputParam = { String name ->
    def envVarName = name.toUpperCase().replace('.', '_').replace('-', '_')
    return System.getProperty(name) ?: System.getenv(envVarName)
}

// finish early if injection is disabled
def gradleInjectionEnabled = getInputParam("gradle-enterprise.injection-enabled")
if (gradleInjectionEnabled != "true") {
    return
}

def geUrl = getInputParam('gradle-enterprise.url')
def geAllowUntrustedServer = Boolean.parseBoolean(getInputParam('gradle-enterprise.allow-untrusted-server'))
def geEnforceUrl = Boolean.parseBoolean(getInputParam('gradle-enterprise.enforce-url'))
def buildScanUploadInBackground = Boolean.parseBoolean(getInputParam('gradle-enterprise.build-scan.upload-in-background'))
def gePluginVersion = getInputParam('gradle-enterprise.plugin.version')
def ccudPluginVersion = getInputParam('gradle-enterprise.ccud-plugin.version')

def atLeastGradle4 = GradleVersion.current() >= GradleVersion.version('4.0')

// finish early if configuration parameters passed in via system properties are not valid/supported
if (ccudPluginVersion && isNotAtLeast(ccudPluginVersion, '1.7')) {
    logger.warn("Common Custom User Data Gradle plugin must be at least 1.7. Configured version is $ccudPluginVersion.")
    return
}

// register buildScanPublished listener and optionally apply the GE / Build Scan plugin
if (GradleVersion.current() < GradleVersion.version('6.0')) {
    rootProject {
        buildscript.configurations.getByName("classpath").incoming.afterResolve { ResolvableDependencies incoming ->
            def resolutionResult = incoming.resolutionResult

            if (gePluginVersion) {
                def scanPluginComponent = resolutionResult.allComponents.find {
                    it.moduleVersion.with { group == "com.gradle" && (name == "build-scan-plugin" || name == "gradle-enterprise-gradle-plugin") }
                }
                if (!scanPluginComponent) {
                    logger.quiet("Applying $BUILD_SCAN_PLUGIN_CLASS via init script")
                    logger.quiet("Connection to Gradle Enterprise: $geUrl, allowUntrustedServer: $geAllowUntrustedServer")
                    applyPluginExternally(pluginManager, BUILD_SCAN_PLUGIN_CLASS)
                    buildScan.server = geUrl
                    buildScan.allowUntrustedServer = geAllowUntrustedServer
                    buildScan.publishAlways()
                    if (buildScan.metaClass.respondsTo(buildScan, 'setUploadInBackground', Boolean)) buildScan.uploadInBackground = buildScanUploadInBackground  // uploadInBackground not available for build-scan-plugin 1.16
                    buildScan.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, CI_AUTO_INJECTION_CUSTOM_VALUE_VALUE
                }

                if (geUrl && geEnforceUrl) {
                    pluginManager.withPlugin(BUILD_SCAN_PLUGIN_ID) {
                        afterEvaluate {
                            logger.quiet("Enforcing Gradle Enterprise: $geUrl, allowUntrustedServer: $geAllowUntrustedServer")
                            buildScan.server = geUrl
                            buildScan.allowUntrustedServer = geAllowUntrustedServer
                        }
                    }
                }
            }

            if (ccudPluginVersion && atLeastGradle4) {
                def ccudPluginComponent = resolutionResult.allComponents.find {
                    it.moduleVersion.with { group == "com.gradle" && name == "common-custom-user-data-gradle-plugin" }
                }
                if (!ccudPluginComponent) {
                    logger.quiet("Applying $CCUD_PLUGIN_CLASS via init script")
                    pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS))
                }
            }
        }
    }
} else {
    gradle.settingsEvaluated { settings ->
        if (gePluginVersion) {
            if (!settings.pluginManager.hasPlugin(GRADLE_ENTERPRISE_PLUGIN_ID)) {
                logger.quiet("Applying $GRADLE_ENTERPRISE_PLUGIN_CLASS via init script")
                logger.quiet("Connection to Gradle Enterprise: $geUrl, allowUntrustedServer: $geAllowUntrustedServer")
                applyPluginExternally(settings.pluginManager, GRADLE_ENTERPRISE_PLUGIN_CLASS)
                extensionsWithPublicType(settings, GRADLE_ENTERPRISE_EXTENSION_CLASS).collect { settings[it.name] }.each { ext ->
                    ext.server = geUrl
                    ext.allowUntrustedServer = geAllowUntrustedServer
                    ext.buildScan.publishAlways()
                    ext.buildScan.uploadInBackground = buildScanUploadInBackground
                    ext.buildScan.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, CI_AUTO_INJECTION_CUSTOM_VALUE_VALUE
                }
            }

            if (geUrl && geEnforceUrl) {
                extensionsWithPublicType(settings, GRADLE_ENTERPRISE_EXTENSION_CLASS).collect { settings[it.name] }.each { ext ->
                    logger.quiet("Enforcing Gradle Enterprise: $geUrl, allowUntrustedServer: $geAllowUntrustedServer")
                    ext.server = geUrl
                    ext.allowUntrustedServer = geAllowUntrustedServer
                }
            }
        }

        if (ccudPluginVersion) {
            if (!settings.pluginManager.hasPlugin(CCUD_PLUGIN_ID)) {
                logger.quiet("Applying $CCUD_PLUGIN_CLASS via init script")
                settings.pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS))
            }
        }
    }
}

void applyPluginExternally(PluginManager pluginManager, String pluginClassName) {
    def externallyApplied = 'gradle.enterprise.externally-applied'
    def oldValue = System.getProperty(externallyApplied)
    System.setProperty(externallyApplied, 'true')
    try {
        pluginManager.apply(initscript.classLoader.loadClass(pluginClassName))
    } finally {
        if (oldValue == null) {
            System.clearProperty(externallyApplied)
        } else {
            System.setProperty(externallyApplied, oldValue)
        }
    }
}

static def extensionsWithPublicType(def container, String publicType) {
    container.extensions.extensionsSchema.elements.findAll { it.publicType.concreteClass.name == publicType }
}

static boolean isNotAtLeast(String versionUnderTest, String referenceVersion) {
    GradleVersion.version(versionUnderTest) < GradleVersion.version(referenceVersion)
}