/*
 * Copyright 2021 Aiven Oy
 *
 * Licensed 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.
 */

buildscript {
    repositories {
        mavenCentral()
    }
}

plugins {
    id 'info.solidsoft.pitest' version '1.15.0' apply false
    id "java-library"
    id "distribution"
}

apply plugin: 'info.solidsoft.pitest.aggregator'

wrapper {
    distributionType = 'ALL'
    doLast {
        def sha256Sum = new String(new URL("${distributionUrl}.sha256").bytes)
        propertiesFile << "distributionSha256Sum=${sha256Sum}\n"
        println "Added checksum to wrapper properties"
    }
}

repositories {
    mavenCentral()
}

subprojects {
    // https://docs.gradle.org/current/userguide/java_library_plugin.html
    apply plugin: "java"

    // https://docs.gradle.org/current/userguide/checkstyle_plugin.html
    apply plugin: "checkstyle"

    // https://docs.gradle.org/current/userguide/jacoco_plugin.html
    apply plugin: "jacoco"

    // https://docs.gradle.org/current/userguide/distribution_plugin.html
    apply plugin: "distribution"

    apply plugin: "idea"
    apply plugin: 'info.solidsoft.pitest'

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

    repositories {
        // Use Maven Central for resolving dependencies.
        mavenCentral()
        flatDir {
            dirs rootProject.file('lib')
        }
        // TODO remove when we reorganize structure providers
        // ---
        // The following repo is needed only for the structure providers.
        // Keep this in mind when extracting them into a separate project.
        maven {
            url "https://packages.confluent.io/maven"
        }
        // ---
    }

    java {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17

        withJavadocJar()
        withSourcesJar()
    }

    checkstyle {
        toolVersion = "10.12.0"
        configDirectory = rootProject.file("checkstyle/")
    }

    pitest {
        //adds dependency to org.pitest:pitest-junit5-plugin and sets "testPlugin" to "junit5"
        junit5PluginVersion = '1.2.0'
        pitestVersion = '1.16.3'
        targetClasses.add("io.aiven.kafka.tieredstorage.*")  //by default "${project.group}.*"
        targetTests.add("io.aiven.kafka.tieredstorage.*")  //by default "${project.group}.*"
        threads = Runtime.getRuntime().availableProcessors()
        outputFormats.addAll('XML', 'HTML')
        mutators.add('ALL')
        exportLineCoverage = true
        addJUnitPlatformLauncher = false
        if (project.name in ['storage']) {
            failWhenNoMutations = false
        }
    }


    ext {
        // Keep empty lines between versions to avoid conflicts on mass update (e.g. Dependabot).

        junitVersion = "5.13.4"

        junitPlatformVersion = "6.0.0"

        mockitoVersion = "5.20.0"

        slf4jVersion = "2.0.13"

        kafkaVersion = "3.8.0"

        assertJVersion = "3.27.3"

        apacheCommonsIOVersion = "2.19.0"

        jacksonVersion = "2.20.0"

        awaitilityVersion = "4.3.0"

        awsSdkVersion = "2.34.7"

        gcpSdkVersion = "2.58.0"

        azureSdkVersion = "1.2.28"

        testcontainersVersion = "1.21.1"

        testcontainersFakeGcsServerVersion = "0.2.0"

        bucket4jVersion = "8.14.0"
    }

    dependencies {
        compileOnly "org.apache.kafka:kafka-clients:$kafkaVersion"
        compileOnly "org.apache.kafka:kafka-storage-api:$kafkaVersion"

        compileOnly "org.slf4j:slf4j-api:$slf4jVersion"

        testImplementation "org.apache.kafka:kafka-clients:$kafkaVersion"
        testImplementation "org.apache.kafka:kafka-storage-api:$kafkaVersion"

        testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
        testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
        testImplementation 'org.junit.platform:junit-platform-launcher:$junitPlatformVersion'
        testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"

        testImplementation "org.assertj:assertj-core:$assertJVersion"

        testImplementation "org.mockito:mockito-core:$mockitoVersion"
        testImplementation "org.mockito:mockito-junit-jupiter:$mockitoVersion"

        testImplementation "org.awaitility:awaitility:$awaitilityVersion"

        testRuntimeOnly "org.slf4j:slf4j-log4j12:$slf4jVersion"
    }

    tasks.named('test') {
        // Use junit platform for unit tests.
        useJUnitPlatform()
    }

    sourceSets {
        integrationTest {
            java {
                srcDirs = ['src/integration-test/java']
            }
            resources {
                srcDirs = ['src/integration-test/resources']
            }

            compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath
            runtimeClasspath += output + compileClasspath
        }
    }

    idea {
        module {
            testSourceDirs += sourceSets.integrationTest.java.srcDirs
            testSourceDirs += sourceSets.integrationTest.resources.srcDirs
        }
    }

    configurations {
        integrationTestImplementation.extendsFrom testImplementation
        integrationTestRuntime.extendsFrom testRuntimeClasspath
    }

    tasks.register('integrationTest', Test) {
        description = 'Runs the integration tests.'
        group = 'verification'
        testClassesDirs = sourceSets.integrationTest.output.classesDirs
        classpath = sourceSets.integrationTest.runtimeClasspath

        shouldRunAfter test, distTar

        useJUnitPlatform()

        // Run always.
        outputs.upToDateWhen { false }
    }

    check.configure {
        dependsOn tasks.named("integrationTest")
    }

    tasks.javadoc {
        // disable missing javadoc lint and show only warning and error messages
        options.addStringOption('Xdoclint:all,-missing', '-quiet')
    }

    distributions {
        main {
            contents {
                from jar
                from sourcesJar
                from configurations.runtimeClasspath
            }
        }
    }

    tasks.distTar {
        compression = Compression.GZIP
        duplicatesStrategy DuplicatesStrategy.EXCLUDE
    }

    publishing {
        repositories {
            maven {
                name="sonatype"

                def releasesRepoUrl = "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/"
                def snapshotsRepoUrl = "https://ossrh-staging-api.central.sonatype.com/content/repositories/snapshots/"
                url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl

                credentials(PasswordCredentials)
            }
        }
        publications {
            mavenJava(MavenPublication) {
                from components.java

                pom {
                    name = 'Aiven Tiered storage for Apache Kafka'
                    description = 'Aiven Tiered storage for Apache Kafka'
                    url = "https://github.com/Aiven-Open/tiered-storage-for-apache-kafka"
                    organization {
                        name = "Aiven Oy"
                        url = "https://aiven.io"
                    }

                    licenses {
                        license {
                            name = "Apache 2.0"
                            url = "http://www.apache.org/licenses/LICENSE-2.0"
                            distribution = "repo"
                        }
                    }

                    developers {
                        developer {
                            id = 'aiven'
                            name = 'Aiven Opensource'
                            email = 'opensource@aiven.io'
                        }
                    }

                    scm {
                        connection = 'scm:git:git://github.com:Aiven-Open/tiered-storage-for-apache-kafka.git'
                        developerConnection = 'scm:git:ssh://github.com:Aiven-Open/tiered-storage-for-apache-kafka.git'
                        url = 'https://github.com/Aiven-Open/tiered-storage-for-apache-kafka'
                    }
                }

                afterEvaluate {
                    groupId = "io.aiven"
                    artifactId = "tiered-storage-for-apache-kafka-${archivesBaseName}"
                }
            }
        }

        signing {
            sign publishing.publications.mavenJava
            useGpgCmd()
            // Some issue in the plugin:
            // GPG outputs already armored signatures. The plugin also does armoring for `asc` files.
            // This results in double armored signatures, i.e. garbage.
            // Override the signature type provider to use unarmored output for `asc` files, which works well with GPG.
            signatureTypes = new AbstractSignatureTypeProvider() {
                {
                    BinarySignatureType binary = new BinarySignatureType() {
                        @Override
                        String getExtension() {
                            return "asc"
                        }
                    }
                    register(binary)
                    setDefaultType(binary.getExtension())
                }
            }
        }
    }

    // Skip test fixtures from distribution archives
    afterEvaluate { proj ->
        // Skip test fixtures from distributions
        proj.distributions.all {
            contents {
                // Exclude any test fixtures JARs from the distribution
                exclude { element ->
                    element.file.name.contains('test-fixtures')
                    element.file.name.contains('sources')
                }
            }
        }
    }
}

distributions {
    main {
        contents {
            from(project(":core").jar)
            from(project(":core").configurations.runtimeClasspath)
            from(project(":storage:core").jar)
            from(project(":storage:core").configurations.runtimeClasspath)
            from(project(":storage:filesystem").jar)
            from(project(":storage:filesystem").configurations.runtimeClasspath)
        }
    }
}

tasks.distTar {
    compression = Compression.GZIP
    duplicatesStrategy DuplicatesStrategy.EXCLUDE
}

tasks.register('copyDistFiles', Copy) {
    from tarTree(tasks.distTar.outputs.files.singleFile)
    into layout.buildDirectory.dir('docker')
    doFirst {
        delete layout.buildDirectory.dir('docker')
    }
    dependsOn tasks.distTar
}

tasks.register('validateDependencies') {
    doLast {
        def dependencyVersions = [:]
        def conflictsFound = false

        subprojects { subproject ->
            println "Module: ${subproject.name}"
            def runtimeClasspath = subproject.sourceSets.main.runtimeClasspath
            runtimeClasspath.each { file ->

                def fileName = file.name
                def matcher = fileName =~ /(.+)-(\d+\.\d+\.\d+(-.+)?)(\..+)/

                if (matcher) {
                    def artifactId = matcher[0][1]
                    def version = matcher[0][2]

                    if (dependencyVersions.containsKey(artifactId)) {
                        if (dependencyVersions[artifactId] != version) {
                            println "Conflict found for $artifactId: ${dependencyVersions[artifactId]} vs $version"
                            conflictsFound = true
                        }
                    } else {
                        dependencyVersions[artifactId] = version
                    }
                }
            }
        }

        assert !conflictsFound : "Dependency conflicts found!"
    }
}

//tasks.named("check") {
//    dependsOn(tasks.named("validateDependencies"))
//}

// Define the list of modules to be published
def modulesToPublish = [
        ':commons',
        ':core',
        ':storage:core',
        ':storage:filesystem',
        ':storage:s3',
        ':storage:gcs',
        ':storage:azure',
]

// Define and configure the publishToSonatype task
tasks.register("publishToSonatype") {
    description = 'Publishes selected modules to Sonatype repository'
    group = 'publishing'
    dependsOn(tasks.named("distTar"))

    doFirst {
        println "Publishing selected modules to Sonatype..."
    }
}

// Configure task dependencies after all projects are evaluated
gradle.projectsEvaluated {
    tasks.named("publishToSonatype") {
        modulesToPublish.each { modulePath ->
            def moduleProject = modulePath == ':' ? rootProject : project(modulePath)
            def taskName = "${moduleProject.path}:publish${modulePath == ':' ? 'Maven' : 'MavenJava'}PublicationToSonatypeRepository"

            try {
                dependsOn(taskName)
                println "Added $taskName"
            } catch (Exception e) {
                println "Warning: Publication task not found for ${modulePath}"
            }
        }
    }
}
