import org.elasticsearch.hadoop.gradle.scala.SparkVariantPlugin

description = "Elasticsearch Spark (for Spark 3.X)"

apply plugin: 'java-library'
apply plugin: 'scala'
apply plugin: 'es.hadoop.build.integration'
apply plugin: 'spark.variants'

sparkVariants {
    capabilityGroup 'org.elasticsearch.spark.sql.variant'
    setDefaultVariant "spark30scala213", spark30Version, scala213Version
    addFeatureVariant "spark30scala212", spark30Version, scala212Version

    all { SparkVariantPlugin.SparkVariant variant ->
        String scalaCompileTaskName = project.sourceSets
                .getByName(variant.getSourceSetName("main"))
                .getCompileTaskName("scala")

        project.configurations {
            create(variant.configuration('embedded')) {
                transitive = false
                canBeResolved = true
            }
            getByName(variant.configuration('implementation')) {
                extendsFrom project.configurations.getByName(variant.configuration('embedded'))
            }
        }

        // Configure main compile task
        project.getTasks().getByName(scalaCompileTaskName) { ScalaCompile compileScala ->
            configure(compileScala.scalaCompileOptions.forkOptions) {
                memoryMaximumSize = '1g'
            }
            compileScala.scalaCompileOptions.additionalParameters = [
                    "-feature",
                    "-unchecked",
                    "-deprecation",
                    "-Xfuture",
                    "-Yno-adapted-args",
                    "-Ywarn-dead-code",
                    "-Ywarn-numeric-widen",
                    "-Xfatal-warnings"
            ]
        }

        dependencies {
            add(variant.configuration('embedded'), project(":elasticsearch-hadoop-mr"))
            add(variant.configuration('embedded'), project(":elasticsearch-spark")) {
                capabilities {
                    requireCapability("org.elasticsearch.spark.variant:$variant.name:$project.version")
                }
            }

            add(variant.configuration('api'), "org.scala-lang:scala-library:$variant.scalaVersion")
            add(variant.configuration('api'), "org.scala-lang:scala-reflect:$variant.scalaVersion")
            add(variant.configuration('api'), "org.apache.spark:spark-core_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'javax.servlet'
                exclude group: 'org.apache.hadoop'
            }

            add(variant.configuration('implementation'), "org.apache.spark:spark-sql_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'org.apache.hadoop'
            }
            add(variant.configuration('implementation'), "org.apache.spark:spark-streaming_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'org.apache.hadoop'
            }
            add(variant.configuration('implementation'), "org.slf4j:slf4j-api:${project.ext.slf4jVersion}") {
                because 'spark exposes slf4j components in traits that we need to extend'
            }
            add(variant.configuration('implementation'), "commons-logging:commons-logging:1.1.1")
            add(variant.configuration('implementation'), "javax.xml.bind:jaxb-api:2.3.1")
            add(variant.configuration('implementation'), "org.apache.spark:spark-catalyst_${variant.scalaMajorVersion}:$variant.sparkVersion")
            add(variant.configuration('implementation'), "org.apache.spark:spark-yarn_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'org.apache.hadoop'
            }

            // Scala compiler needs these for arcane reasons, but they are not used in the api nor the runtime
            add(variant.configuration('compileOnly'), "org.apache.hadoop.thirdparty:hadoop-shaded-protobuf_3_7:1.0.0")
            add(variant.configuration('compileOnly'), "com.fasterxml.jackson.core:jackson-annotations:2.6.7")
            add(variant.configuration('compileOnly'), "org.json4s:json4s-jackson_${variant.scalaMajorVersion}:3.6.6")
            add(variant.configuration('compileOnly'), "org.json4s:json4s-ast_${variant.scalaMajorVersion}:3.6.6")
            add(variant.configuration('compileOnly'), "org.apache.spark:spark-tags_${variant.scalaMajorVersion}:$variant.sparkVersion")

            add(variant.configuration('test', 'implementation'), project(":test:shared"))
            add(variant.configuration('test', 'implementation'), "jakarta.servlet:jakarta.servlet-api:4.0.3")
            add(variant.configuration('test', 'implementation'), "org.elasticsearch:securemock:1.2")
            add(variant.configuration('test', 'implementation'), "org.apache.spark:spark-core_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'javax.servlet'
                exclude group: 'org.apache.hadoop'
            }
            add(variant.configuration('test', 'implementation'), "org.apache.spark:spark-sql_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'org.apache.hadoop'
            }

            add(variant.configuration('itest', 'implementation'), project(":test:shared"))
            add(variant.configuration('itest', 'implementation'), "jakarta.servlet:jakarta.servlet-api:4.0.3")
            add(variant.configuration('itest', 'implementation'), "org.apache.spark:spark-yarn_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'org.apache.hadoop'
            }
            add(variant.configuration('itest', 'implementation'), "org.apache.spark:spark-streaming_${variant.scalaMajorVersion}:$variant.sparkVersion") {
                exclude group: 'org.apache.hadoop'
            }

            add(variant.configuration('additionalSources'), project(":elasticsearch-hadoop-mr"))
            add(variant.configuration('javadocSources'), project(":elasticsearch-hadoop-mr"))

            add(variant.configuration('additionalSources'), project(":elasticsearch-spark")) {
                capabilities {
                    requireCapability("org.elasticsearch.spark.variant:$variant.name:$project.version")
                }
            }
            add(variant.configuration('javadocSources'), project(":elasticsearch-spark")) {
                capabilities {
                    requireCapability("org.elasticsearch.spark.variant:$variant.name:$project.version")
                }
            }
        }

        def javaFilesOnly = { FileTreeElement spec ->
            spec.file.name.endsWith('.java') || spec.isDirectory()
        }

        // Add java files from scala source set to javadocSourceElements.
        project.fileTree("src/main/scala").include(javaFilesOnly).each {
            project.artifacts.add(variant.configuration('javadocSourceElements'), it)
        }

        // Configure java source generation for javadoc purposes
        String generatedJavaDirectory = "$buildDir/generated/java/${variant.name}"
        Configuration scalaCompilerPlugin = project.configurations.maybeCreate(variant.configuration('scalaCompilerPlugin'))
        scalaCompilerPlugin.defaultDependencies { dependencies ->
            dependencies.add(project.dependencies.create("com.typesafe.genjavadoc:genjavadoc-plugin_${variant.scalaVersion}:0.18"))
        }

        ScalaCompile compileScala = tasks.getByName(scalaCompileTaskName) as ScalaCompile
        compileScala.scalaCompileOptions.with {
            additionalParameters = [
                    "-Xplugin:" + configurations.getByName(variant.configuration('scalaCompilerPlugin')).asPath,
                    "-P:genjavadoc:out=$generatedJavaDirectory".toString()
            ]
        }
        // Export generated Java code from the genjavadoc compiler plugin
        artifacts {
            add(variant.configuration('javadocSourceElements'), project.file(generatedJavaDirectory)) {
                builtBy compileScala
            }
        }
        tasks.getByName(variant.taskName('javadoc')) {
            dependsOn compileScala
            source(generatedJavaDirectory)
        }

        scaladoc {
            title = "${rootProject.description} ${version} API"
        }
    }
}

tasks.withType(ScalaCompile) { ScalaCompile task ->
    task.scalaCompileOptions.additionalParameters = ["-javabootclasspath", new File(project.ext.runtimeJavaHome, 'jre/lib/rt.jar').absolutePath]
    task.options.bootstrapClasspath = layout.files(new File(project.ext.runtimeJavaHome, 'jre/lib/rt.jar'))
    task.sourceCompatibility = project.ext.minimumRuntimeVersion
    task.targetCompatibility = project.ext.minimumRuntimeVersion
    task.options.forkOptions.executable = new File(project.ext.runtimeJavaHome, 'bin/java').absolutePath
}

if (JavaVersion.current() >= JavaVersion.VERSION_16) {
    tasks.withType(Test) { Test task ->
        if (task.getName().startsWith("test"))
        task.configure {
            jvmArgs "--add-opens=java.base/java.io=ALL-UNNAMED" // Needed for IOUtils's BYTE_ARRAY_BUFFER reflection
            jvmArgs "--add-opens=java.base/java.nio=ALL-UNNAMED" // Needed for org.apache.spark.SparkConf, which indirectly uses java.nio.DirectByteBuffer
            jvmArgs "--add-opens=java.base/java.lang=ALL-UNNAMED" // Needed for secure mock
        }}
}

// Embed the embedded dependencies in the final jar after all configuration is complete
sparkVariants {
    all { SparkVariantPlugin.SparkVariant variant ->
        tasks.getByName(variant.taskName('jar')) {
            dependsOn(project.configurations.getByName(variant.configuration('embedded')))
            // TODO: Is there a way to do this lazily? This looks like it resolves the configuration.
            from(project.configurations.getByName(variant.configuration('embedded')).collect { it.isDirectory() ? it : zipTree(it)}) {
                include "org/elasticsearch/**"
                include "esh-build.properties"
                include "META-INF/services/*"
            }
        }
    }
}


tasks.register('copyPoms', Copy) {
    from(tasks.named('generatePomFileForMainPublication')) {
        rename 'pom-default.xml', "elasticsearch-spark-30_2.13-${project.getVersion()}.pom"
    }
    from(tasks.named('generatePomFileForSpark30scala212Publication')) {
        rename 'pom-default.xml', "elasticsearch-spark-30_2.12-${project.getVersion()}.pom"
    }
    into(new File(project.buildDir, 'distributions'))
}

tasks.named('distribution').configure {
    dependsOn 'copyPoms'
}
