/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */

plugins {
    id 'java-platform'
    id 'maven-publish'
    id 'org.apache.grails.buildsrc.publish'
    id 'org.apache.grails.buildsrc.sbom'
}

version = projectVersion
group = 'org.apache.grails'

javaPlatform {
    allowDependencies()
}

ext {
    isReleaseBuild = System.getenv('GRAILS_PUBLISH_RELEASE') == 'true'
    isPublishedExternal = System.getenv().containsKey('NEXUS_PUBLISH_STAGING_PROFILE_ID')
}

dependencies {
    api platform(gradleBomPlatformDependencies['spring-boot-bom']), {
        exclude group: 'org.apache.groovy'
        exclude group: 'org.spockframework'
        exclude group: 'com.fasterxml.jackson'
    }

    // Because gradle uses groovy 3, we must use groovy 3 compatible versions for the grails-gradle-bom
    api platform("org.codehaus.groovy:groovy-bom:${GroovySystem.version}")
    api platform(gradleBomPlatformDependencies['gradle-spock-bom'])

    constraints {
        for (def entry : gradleBomDependencies.entrySet()) {
            api entry.value
        }

        for (Project subproject : rootProject.subprojects) {
            if (subproject.path == project.path) {
                continue // ignore self
            }

            api project(":${subproject.path}")
        }
    }
}

def noSnapshotDependenciesTask = tasks.register('validateNoSnapshotDependencies')
noSnapshotDependenciesTask.configure {
    it.group = 'publishing'
    it.description = 'Validates that no snapshot dependencies are present in the project when performing a release.'

    doLast {
        configurations.each { config ->
            config.allDependencies.each { dep ->
                if (dep.version && dep.version.contains('-SNAPSHOT')) {
                    throw new GradleException("Releases cannot have a snapshot dependency: ${dep.group}:${dep.name} (${dep.version})")
                }
            }
        }
    }
}

if (ext.isReleaseBuild && ext.isPublishedExternal) {
    project.afterEvaluate {
        // after eval is required since publications aren't added until after evaluation
        tasks.named('generateMetadataFileForMavenPublication').configure {
            dependsOn(noSnapshotDependenciesTask)
        }
        tasks.named('generatePomFileForMavenPublication').configure {
            dependsOn(noSnapshotDependenciesTask)
        }
    }
}

Closure determinePropertyName = { String groupId, String artifactId, String version, boolean isBom ->
    Project foundProject = rootProject.subprojects.find {
        it.name == artifactId &&
                it.group == groupId
        it.version == version
    }
    if (foundProject) {
        return "${artifactId.replaceAll('-', '.')}.version" as String
    }

    Map<String, String> toSearch = isBom ? gradleBomPlatformDependencies : gradleBomDependencies

    Map.Entry<String, String> found = toSearch.entrySet().find { "$groupId:$artifactId:$version" as String == it.value as String }
    if (!found) {
        return null
    }

    Map<String, String> versions = gradleBomDependencyVersions
    String possibleKey = found.key.toString()
    while (possibleKey != null) {
        String propertyName = "${possibleKey}.version" as String
        if (versions.containsKey(propertyName)) {
            return propertyName
        }

        int lastIndex = possibleKey.lastIndexOf('-')
        possibleKey = lastIndex > 0 ? possibleKey.substring(0, lastIndex) : null
    }

    throw new GradleException("Could not determine artifact property key for $groupId:$artifactId:$version")
}

ext {
    pomDescription = 'Grails Gradle BOM (Bill of Materials) for managing dependency versions used in Gradle builds for Grails Projects'
    pomCustomization = { xml ->
        def root = xml.asNode()

        def propertiesNode = root.properties ? root.properties[0] : root.appendNode('properties')

        def depMgmt = root.dependencyManagement?.getAt(0)
        def deps = depMgmt?.dependencies?.getAt(0)
        if (deps) {
            Map<String, String> pomProperties = [:]
            deps.dependency.each { dep ->
                String groupId = dep.groupId.text().trim()
                String artifactId = dep.artifactId.text().trim()
                boolean isBom = dep.scope.text().trim() == 'import'

                String inlineVersion = dep.version.text().trim()
                if (inlineVersion == 'null') {
                    inlineVersion = null
                }

                if (inlineVersion) {
                    String propertyName = determinePropertyName(groupId, artifactId, inlineVersion, isBom)
                    if (propertyName) {
                        // Replace the version in the pom with a property reference
                        String propertyReference = "\${${propertyName}}"
                        dep.version[0].value = propertyReference

                        // Add an entry in the <properties> node with the actual version number
                        pomProperties.put(propertyName, inlineVersion)
                    }
                } else if (!inlineVersion) {
                    throw new GradleException('Dependency $groupId:$artifactId does not have a version.')
                }
            }

            for (Map.Entry<String, String> property : pomProperties.entrySet()) {
                propertiesNode.appendNode(property.key, property.value)
            }
        }
    }
}
