import io.gitlab.arturbosch.detekt.Detekt

import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit

plugins {
    id 'java'
    id 'groovy'
    id 'idea'
    id 'application'
    id 'jacoco'

    alias(libs.plugins.kotlin)
    alias(libs.plugins.jib)
    alias(libs.plugins.spring)
    alias(libs.plugins.git)
    alias(libs.plugins.protobuf)
    alias(libs.plugins.ktlint)
    alias(libs.plugins.detekt)
}


group = 'io.emeraldpay.dshackle'

// Version schema:
//    x.x.x for production, following SemVer model
//    x.x.x-SNAPSHOT for development
version = '0.13.0-SNAPSHOT'

java {
    sourceCompatibility = JavaVersion.VERSION_13
    targetCompatibility = JavaVersion.VERSION_13
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url "https://repo.spring.io/snapshot" }
    maven { url "https://repo.spring.io/milestone" }
    maven { url "https://maven.emrld.io" }
}

configurations {
    compile.exclude group: "commons-logging"
    compile.exclude group: "ch.qos.logback"
    compile.exclude group: "org.slf4j", module: "slf4j-jdk14"
    compile.exclude group: "org.slf4j", module: "log4j-over-slf4j"
    // should be used only for generation of the stubs, the lib contains grpc classes
    compile.exclude group: "com.salesforce.servicelibs", module: "reactor-grpc"
}

dependencies {
    implementation libs.bundles.kotlin

    implementation libs.bundles.grpc
    implementation libs.bundles.netty
    implementation libs.zeromq
    implementation(libs.bundles.spring.framework) {
        exclude module: 'spring-boot-starter-logging'
    }

    implementation libs.bundles.reactor

    implementation(libs.reactor.grpc.stub)

    implementation libs.micrometer.registry.prometheus
    implementation libs.lettuce.core

    implementation libs.bundles.etherjar

    implementation(libs.emerald.api) {
        exclude group: 'com.salesforce.servicelibs', module: 'reactor-grpc'
    }

    implementation libs.bitcoinj

    implementation libs.snake.yaml
    implementation libs.bundles.httpcomponents
    implementation libs.bundles.jackson

    implementation libs.bundles.apache.commons
    implementation libs.bouncycastle
    implementation libs.caffeine
    implementation libs.javax.annotations

    implementation libs.bundles.slf4j

    testImplementation libs.cglib.nodep
    testImplementation libs.spockframework.core
    testImplementation libs.grpc.testing
    testImplementation libs.reactor.test
    testImplementation libs.objgenesis
    testImplementation libs.mockserver.netty
    testImplementation libs.java.websocket
    testImplementation libs.equals.verifier
    testImplementation libs.groovy

    detektPlugins libs.detekt.formatting
}

compileKotlin {
    kotlinOptions {
        jvmTarget = "13"
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = "13"
    }
}

test {
    jvmArgs '-ea'
    testLogging.showStandardStreams = false
    testLogging.exceptionFormat = 'full'
    finalizedBy jacocoTestReport
    useJUnitPlatform()

    // getting on CI:
    //   java.security.KeyStoreException: Key protection algorithm not found: java.security.UnrecoverableKeyException: Encrypt Private Key failed: unrecognized algorithm name: PBEWithSHA1AndDESede
    //            at java.base/sun.security.pkcs12.PKCS12KeyStore.setKeyEntry(PKCS12KeyStore.java:700)
    //            at java.base/sun.security.pkcs12.PKCS12KeyStore.engineSetKeyEntry(PKCS12KeyStore.java:597)
    //            at java.base/sun.security.util.KeyStoreDelegator.engineSetKeyEntry(KeyStoreDelegator.java:111)
    //            at java.base/java.security.KeyStore.setKeyEntry(KeyStore.java:1167)
    //            at io.netty.handler.ssl.SslContext.buildKeyStore(SslContext.java:1102)
    //            at io.netty.handler.ssl.ReferenceCountedOpenSslServerContext.newSessionContext(ReferenceCountedOpenSslServerContext.java:123)
    // ----
    // see
    // https://github.com/bcgit/bc-java/issues/941
    // https://bugs.openjdk.java.net/browse/JDK-8266279
    //
    systemProperty "keystore.pkcs12.keyProtectionAlgorithm", "PBEWithHmacSHA256AndAES_256"
}

application {
    getMainClass().set('io.emeraldpay.dshackle.StarterKt')
}

jib {
    from {
        image = 'openjdk:13'
    }
    to {
        // by default publish as:
        //  dshackle:shapshot,
        //  dshackle:t<yyyyMMddHHmm>,
        //  dshackle:<versionFull> and
        //  dshackle:<versionMajor.versionMinor>
        //  dshackle:latest only when publishing first version with zero patch (ex. 1.2.0)
        image = [
                project.hasProperty('docker') ? project.property('docker') : 'emeraldpay',
                '/dshackle:',
                'snapshot'
        ].join('')
        tags = [project.version].with(true) {
            add "t" + DateTimeFormatter.ofPattern("yyyyMMddHHmm").withZone(ZoneId.of('UTC')).format(Instant.now())
            add project.version.toString().replaceAll('(\\d+\\.\\d+).+', '$1')
        }
        auth {
            username = 'splix'
            password = System.getenv('DOCKER_KEY')
        }
    }
    container {
        jvmFlags = ['-Xms1024m', '-XX:MaxRAMPercentage=60']
        mainClass = 'io.emeraldpay.dshackle.StarterKt'
        args = []
        ports = ['2448', '2449', '8545']
    }
}

jar {
    enabled = true
}

afterEvaluate {
    distZip.dependsOn(jar)
    compileKotlin.dependsOn(generateVersion)
    jar.dependsOn(generateVersion)
}

protobuf {
    protoc {
        // if $PROTOC_PATH is set then locally installed protoc is used otherwise it is downloaded remotely
        path = System.getenv("PROTOC_PATH") ?: null
        artifact = System.getenv("PROTOC_PATH") == null ? "com.google.protobuf:protoc:${libs.versions.protoc.get()}" : null
    }
    plugins {
    }
    generateProtoTasks {
    }
}

sourceSets {
    main {
        resources.srcDirs += project.buildDir.absolutePath + "/generated/version"
    }
}

task generateVersion() {
    group = 'Build'
    description = 'Generate project version'

    doLast {
        def version = versionDetails()
        def resourcesDir = new File(project.buildDir.absolutePath + "/generated/version")
        resourcesDir.mkdirs()
        new File(resourcesDir, "version.properties").text = [
                "# AUTOMATICALLY GENERATED",
                "version.app=$project.version",
                "version.commit=${version.gitHash}",
                "version.tag=${version.lastTag}",
                "version.date=${DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.of('UTC')).format(Instant.now().truncatedTo(ChronoUnit.SECONDS))} UTC"
        ].join("\n")
    }
}

// Show the list of failed tests and output only for them, helpful for CI
ext.failedTests = []
tasks.withType(Test) {
    def stdout = new LinkedList<String>()
    beforeTest { TestDescriptor td ->
        stdout.clear()
    }
    onOutput { TestDescriptor td, TestOutputEvent toe ->
        stdout.addAll(toe.getMessage().split('(?m)$'))
        while (stdout.size() > 100) {
            stdout.remove()
        }
    }
    afterTest { TestDescriptor descriptor, TestResult result ->
        if (result.resultType == org.gradle.api.tasks.testing.TestResult.ResultType.FAILURE) {
            failedTests << "${descriptor.className} > ${descriptor.name}"
            if (!stdout.isEmpty()) {
                println("-------- ${descriptor.className} > ${descriptor.name} OUTPUT ".padRight(120, "-"))
                stdout.each { print(it) }
                println("================".padRight(120, "="))
            }
        }
    }
}
gradle.buildFinished {
    if (!failedTests.empty) {
        println "Failed tests for ${project.name}:"
        failedTests.each { failedTest ->
            println failedTest
        }
        println ""
    }
}

jacocoTestReport {
    dependsOn test
    reports {
        xml.required.set true
        html.required.set true
    }
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: 'io/emeraldpay/dshackle/proto/**')
        }))
    }
}
jacoco {
    toolVersion = "0.8.7"
}

detekt {
    toolVersion = "1.18.1"
    parallel = false
    basePath = projectDir
    debug = false
    ignoreFailures = true

    reports {
        xml {
            enabled = true
            destination = file("build/reports/detekt/detekt.xml")
        }
        html {
            enabled = true
            destination = file("build/reports/detekt/detekt.html")
        }
    }
}

tasks.withType(Detekt).configureEach {
    jvmTarget = "13"
}
