/*
 * 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.
 */

/**
 * The sole purpose of this sub-project is to produce a shaded package for the vertx-client. Currently,
 * the shadow plugin has some limitations when producing a non-shaded and shaded versions of the jar.
 */

import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipOutputStream

import java.nio.file.Paths

plugins {
    id('java-library')
    id('maven-publish')
    id('com.gradleup.shadow') version '8.3.0'
}

version project.version

sourceCompatibility = 1.8

configurations {
    all*.exclude(group: 'ch.qos.logback')
}

dependencies {
    shadow(group: 'org.slf4j', name: 'slf4j-api', version: "${project.slf4jVersion}")
    api(project(':vertx-client'))

    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
    testImplementation(project(path: ':vertx-client-shaded', configuration: 'shadow'))
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
    reports {
        junitXml.setRequired(true)
        def destDir = Paths.get(rootProject.rootDir.absolutePath, "build", "test-results", "vertx-client-shaded").toFile()
        println("Destination directory for vertx-client-shaded tests: ${destDir}")
        junitXml.getOutputLocation().set(destDir)
        html.setRequired(true)
        html.getOutputLocation().set(destDir)
    }
}

// Relocating a Package
shadowJar {
    archiveClassifier.set('')
    // Our use of Jackson should be an implementation detail - shade everything so no matter what
    // version of Jackson is available in the classpath we don't break consumers of the client
    relocate 'org.apache.cassandra.sidecar.common', 'o.a.c.sidecar.client.shaded.common'
    relocate 'com.fasterxml.jackson', 'o.a.c.sidecar.client.shaded.com.fasterxml.jackson'
    relocate 'io.netty', 'o.a.c.sidecar.client.shaded.io.netty'
    relocate 'io.vertx', 'o.a.c.sidecar.client.shaded.io.vertx'
    relocate 'META-INF/native/libnetty', 'META-INF/native/libo_a_c_sidecar_client_shaded_netty'
    relocate 'META-INF/versions/11/io/vertx', 'META-INF/versions/11/o/a/c/sidecar/client/shaded/io/vertx'
    transform(NettyResourceTransformer.class)
    mergeServiceFiles()

    dependencies {
        exclude(dependency('org.slf4j:.*:.*'))
    }

    dependsOn assemble
}

tasks.named('test') {
    dependsOn 'shadowJar'
}

ext {
    mavenRepositoryUrl = propertyWithDefault("maven.repository.url", "")
    // 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
    // -Pmaven.username and -Pmaven.password
    mavenUsername = propertyWithDefault("maven.username", "")
    mavenPassword = propertyWithDefault("maven.password", "")
}

publishing {
    publications {
        shadow(MavenPublication) { publication ->
            project.shadow.component(publication)
            groupId rootProject.group
            artifactId "sidecar-vertx-client-all"
            version System.getenv("CODE_VERSION") ?: "${project.version}"

            if (mavenRepositoryUrl != "") {
                pom {
                    url = "https://cassandra.apache.org/"
                    licenses {
                        license {
                            name = "The Apache License, Version 2.0"
                            url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
                            distribution = "repo"
                        }
                    }
                }
            }
        }
    }

    if (mavenRepositoryUrl != "") {
        repositories {
            maven {
                url = mavenRepositoryUrl
                if (mavenUsername != "" && mavenPassword != "") {
                    credentials {
                        username = mavenUsername
                        password = mavenPassword
                    }
                }
            }
        }
    }
}

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

    sign publishing.publications
}

/**
 * A Transformer which updates the Netty JAR META-INF/ resources to accurately
 * reference shaded class names.
 */
@CacheableTransformer
class NettyResourceTransformer implements Transformer {

    // A map of resource file paths to be modified
    private Map<String, String> resources = [:]

    @Override
    boolean canTransformResource(FileTreeElement fileTreeElement) {
        fileTreeElement.name.startsWith("META-INF/native-image/io.netty")
    }

    @Override
    void transform(TransformerContext context) {
        String updatedPath = context.path.replace("io.netty", "o.a.c.sidecar.client.shaded.io.netty")
        String updatedContent = context.is.getText().replace("io.netty", "o.a.c.sidecar.client.shaded.io.netty")
        resources.put(updatedPath, updatedContent)
    }

    @Override
    boolean hasTransformedResource() {
        resources.size() > 0
    }

    @Override
    void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) {
        for (resourceEntry in resources) {
            ZipEntry entry = new ZipEntry(resourceEntry.key)
            entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time)

            jos.putNextEntry(entry)
            jos.write(resourceEntry.value.getBytes())
            jos.closeEntry()
        }
    }
}
