// 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
//
//   http://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.

// This file contains common tasks and configuration for artifact publishing.

apply plugin: "maven-publish"
apply plugin: "signing"

ext {
  // This allows simplified builds and local maven installs.
  forceSigning = propertyExists("forceSigning")
  skipSigning = propertyExists("skipSigning")
  shouldSign =
      // Always sign artifacts if -PforceSigning is passed.
      forceSigning ||
          // Skip signing artifacts by default if -PskipSigning is passed.
          (!skipSigning
              // Sign artifacts if the version is not a snapshot, and we are uploading them to maven.
              && !version.endsWith("SNAPSHOT")
              && project.gradle.startParameter.taskNames.any { it.contains("upload") })
  // These properties can be set in ~/.gradle/gradle.properties file,
  // though it would be open text. They can also be set on the cli via
  // -PmavenUsername and -PmavenPassword.
  mavenPublishUrl = propertyWithDefault("mavenPublishUrl", "")
  mavenUsername = propertyWithDefault("mavenUsername", "")
  mavenPassword = propertyWithDefault("mavenPassword", "")
}

publishing {
  publications {
    maven(MavenPublication) {
      from components.java

      groupId "org.apache.kudu"
      version "${project.version}"

      // Adjust the artifact names to include the spark and scala base versions.
      if (["kudu-backup", "kudu-spark"].contains(project.name)) {
        artifactId = "${project.archivesBaseName}${versions.sparkBase}_${versions.scalaBase}"
      }

      if (project.name.equals("kudu-spark-tools")) {
        artifactId = "kudu-spark${versions.sparkBase}-tools_${versions.scalaBase}"
      }

      artifact(sourcesJar) {
        classifier = "sources"
      }
      artifact(testSourcesJar) {
        classifier = "testSources"
      }
      artifact(javadocJar) {
        classifier = "javadoc"
      }

      pom {
        url = "$url"
        licenses {
          license {
            name = "The Apache License, Version 2.0"
            url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
            distribution = "repo"
          }
        }
      }

      // Remove the shaded dependencies from the generated pom.

      pom.withXml {
        def implementationDeps = []
        def shadedDeps = []
        def unshadedDeps = []

        def implConfig = project.configurations.findByName("implementation")
        def shadowConfig = project.configurations.findByName("shadow")
        def unshadedConfig = project.configurations.findByName("compileUnshaded")

        if (implConfig) {
          implementationDeps = implConfig.allDependencies
        }
        if (shadowConfig) {
          shadedDeps = shadowConfig.allDependencies
        }
        if (unshadedConfig) {
          unshadedDeps = unshadedConfig.allDependencies
        }
       // Explanation for the below logic of excluding and including unshadedDeps:
       // Given the following sets:
       //   implDeps     = {A, B, C, D}
       //   shadowDeps   = {D, E, F}
       //   unshadedDeps = {C, D, G}
       //
       // The logic here computes:
       //   filteredImplDeps = implDeps - shadowDeps - unshadedDeps = {A, B}
       //   finalDeps = filteredImplDeps + unshadedDeps = {A, B, C, D, G}
       //
       // This approach avoids inadvertently excluding dependencies like 'C' and 'G'
       // that may appear in unshadedDeps but are not in implDeps.
       // While in our current setup `unshadedDeps ⊆ shadowDeps` due to:
       //   configurations.shadow.extendsFrom(configurations.compileUnshaded)
       // this logic ensures correctness in future scenarios where that might not hold.
       // It also improves clarity and maintainability for future readers.
        def filteredImplDeps = implementationDeps - shadedDeps - unshadedDeps
        def finalDeps = filteredImplDeps + unshadedDeps
        asNode().dependencies.dependency.findAll{
          finalDeps.findAll{ dep ->
            dep.name == it.artifactId*.value()[0][0]
          }
        }.each() {
          logger.info "Removing: ${it.artifactId*.value()[0][0]} from pom"
          assert it.parent().remove(it)
        }
        // Ensure <dependencies> node exists
        def dependenciesNode = asNode().dependencies?.getAt(0)
        if (!dependenciesNode) {
          dependenciesNode = asNode().appendNode("dependencies")
        }

        // Add back unshaded dependencies manually with <scope>compile</scope> (to get proper exclusions)
        unshadedDeps.findAll { it.group && it.name && it.version }.each { dep ->
          logger.info "Adding compileUnshaded: ${dep.group}:${dep.name}:${dep.version}"
          def depNode = dependenciesNode.appendNode("dependency")
          depNode.appendNode("groupId", dep.group)
          depNode.appendNode("artifactId", dep.name)
          depNode.appendNode("version", dep.version)
          depNode.appendNode("scope", "compile")

          if (!dep.excludeRules.isEmpty()) {
            def exclusionsNode = depNode.appendNode("exclusions")
            dep.excludeRules.each { ex ->
              def exclusion = exclusionsNode.appendNode("exclusion")
              exclusion.appendNode("groupId", ex.group ?: "*")
              exclusion.appendNode("artifactId", ex.module ?: "*")
            }
          }
        }
        // TODO(achennaka): Find a cleaner fix to have the scope as compile instead of runtime
        asNode().dependencies.dependency.findAll {
          it.artifactId.text() ==~ /kudu-spark[0-9]*_.*/
        }.each {
          it.scope*.value = "compile"   // or remove it.parent().remove(it) if you want none
        }
      }
    }
  }

  repositories {
    maven {
      url "${mavenPublishUrl}"
      credentials {
        username "${mavenUsername}"
        password "${mavenPassword}"
      }
    }
  }
}

signing {
  required { shouldSign }
  if (shouldSign) {
    useGpgCmd() // Use gpg-agent to sign
  }

  sign configurations.archives
}

// Add the install task to the "Upload" group so it's visible in the tasks output.
publish.group = "Upload"

