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'

    id("org.jetbrains.kotlin.jvm") version "$kotlinVersion"
    id("com.google.cloud.tools.jib") version "3.3.1"
    id("org.springframework.boot") version "$springBootVersion"
    id("com.palantir.git-version") version "0.12.3"
    id("com.google.protobuf") version "0.8.17"
    id("org.jlleitschuh.gradle.ktlint") version "13.1.0"
    id("io.gitlab.arturbosch.detekt") version "$detektVersion"
    id("com.google.osdetector") version "1.7.0"
}


group = 'io.emeraldpay.dshackle'

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

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

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

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

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"

    implementation "io.grpc:grpc-protobuf:$grpcVersion"
    implementation "io.grpc:grpc-stub:$grpcVersion"
    implementation "io.grpc:grpc-netty:$grpcVersion"

    implementation "io.netty:netty-common:$nettyVersion"
    implementation "io.netty:netty-transport:$nettyVersion"
    implementation "io.netty:netty-handler:$nettyVersion"
    implementation "io.netty:netty-handler-proxy:$nettyVersion"
    implementation "io.netty:netty-resolver:$nettyVersion"
    implementation "io.netty:netty-resolver-dns:$nettyVersion"
    implementation "io.netty:netty-codec:$nettyVersion"
    implementation "io.netty:netty-codec-http:$nettyVersion"
    implementation "io.netty:netty-codec-http2:$nettyVersion"
    implementation "io.netty:netty-buffer:$nettyVersion"
    implementation "io.netty:netty-tcnative:$nettyTcnativeVersion"
    implementation "io.netty:netty-tcnative-boringssl-static:$nettyTcnativeVersion"
    implementation "io.netty:netty-tcnative-boringssl-static:$nettyTcnativeVersion:${osdetector.classifier}"

    implementation "org.zeromq:jeromq:0.5.2"

    implementation "org.springframework.boot:spring-boot-starter:$springBootVersion"
    implementation "org.springframework.security:spring-security-core:$springSecurityVersion"
    implementation "org.springframework.security:spring-security-web:$springSecurityVersion"
    implementation "org.springframework.security:spring-security-config:$springSecurityVersion"

    implementation "io.projectreactor:reactor-core:$reactorVersion"
    implementation "io.projectreactor:reactor-tools:$reactorVersion"
    implementation "io.projectreactor.netty:reactor-netty:1.1.7"
    implementation "io.projectreactor.addons:reactor-extra:3.4.5"
    implementation "io.projectreactor.kotlin:reactor-kotlin-extensions:1.1.4"

    implementation "com.salesforce.servicelibs:reactor-grpc-stub:1.2.0"

    implementation "io.micrometer:micrometer-registry-prometheus:1.5.6"
    implementation "io.lettuce:lettuce-core:5.2.2.RELEASE"

    implementation "io.emeraldpay.etherjar:etherjar-domain:$etherjarVersion"
    implementation "io.emeraldpay.etherjar:etherjar-hex:$etherjarVersion"
    implementation "io.emeraldpay.etherjar:etherjar-rpc-api:$etherjarVersion"
    implementation "io.emeraldpay.etherjar:etherjar-rpc-http:$etherjarVersion"
    implementation "io.emeraldpay.etherjar:etherjar-rpc-ws:$etherjarVersion"
    implementation "io.emeraldpay.etherjar:etherjar-tx:$etherjarVersion"
    implementation "io.emeraldpay.etherjar:etherjar-contract:$etherjarVersion"
    implementation "io.emeraldpay.etherjar:etherjar-erc20:$etherjarVersion"

    implementation "io.emeraldpay:emerald-api:0.14.0"

    implementation "org.bitcoinj:bitcoinj-core:0.16.2"

    implementation "org.yaml:snakeyaml:1.24"
    implementation "org.apache.httpcomponents:httpmime:$httpcomponentsVersion"
    implementation "org.apache.httpcomponents:httpclient:$httpcomponentsVersion"

    implementation "com.fasterxml.jackson.core:jackson-core:$jacksonVersion"
    implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
    implementation "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:$jacksonVersion"
    implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion"
    implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion"

    implementation "commons-io:commons-io:2.6"
    implementation "org.apache.commons:commons-lang3:3.9"
    implementation "org.apache.commons:commons-collections4:4.3"

    implementation "org.bouncycastle:bcprov-jdk18on:1.73"
    implementation "com.github.ben-manes.caffeine:caffeine:2.8.5"
    implementation "javax.annotation:javax.annotation-api:1.3.2"

    implementation "org.slf4j:slf4j-api:$slf4jVersion"
    implementation "org.slf4j:jul-to-slf4j:$slf4jVersion"
    implementation "org.slf4j:jcl-over-slf4j:$slf4jVersion"
    implementation "org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0"


    testImplementation "cglib:cglib-nodep:3.3.0"
    testImplementation "org.spockframework:spock-core:2.3-groovy-4.0"
    testImplementation "io.grpc:grpc-testing:$grpcVersion"
    testImplementation "io.grpc:grpc-inprocess:$grpcVersion"
    testImplementation "io.projectreactor:reactor-test:$reactorVersion"
    testImplementation "org.objenesis:objenesis:3.1"
    testImplementation "org.mock-server:mockserver-netty:5.15.0"
    testImplementation "org.java-websocket:Java-WebSocket:1.5.3"
    testImplementation "nl.jqno.equalsverifier:equalsverifier:3.15.4"
    testImplementation "org.apache.groovy:groovy:$groovyVersion"
    testImplementation "org.testcontainers:spock:1.21.3"
    testImplementation "org.junit.jupiter:junit-jupiter-engine:5.9.1"
    testImplementation "io.kotest:kotest-runner-junit5:5.9.1"
    testImplementation "io.kotest:kotest-framework-datatest:5.9.1"
    testImplementation "io.mockk:mockk:1.14.5"
    testImplementation "org.springframework:spring-test:$springVersion"


    detektPlugins "io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion"
}

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

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 = 'azul/zulu-openjdk-debian:21'
    }
    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')
        }
    }
    container {
        jvmFlags = ['-Xms1024m']
        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:$protocVersion" : 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.13"
}

detekt {
    toolVersion = "$detektVersion"
    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 = "21"
}
