plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.2'
    id 'io.openapiprocessor.openapi-processor' version '2022.2'
    id 'io.spring.dependency-management' version '1.0.12.RELEASE'
    id 'com.github.jk1.dependency-license-report' version '2.1'
    id "org.owasp.dependencycheck" version "7.1.1"
    id "com.diffplug.spotless" version "6.9.0"
    id 'jacoco'
}

group = 'net.hostsharing'
version = '0.0.1-SNAPSHOT'

wrapper {
    distributionType = Wrapper.DistributionType.BIN
    gradleVersion = '7.5'
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    testCompile {
        // Only JUNit 5 (Jupiter) should be used at compile time.
        // For runtime it's still needed by testcontainers, though.
        exclude group: 'junit', module: 'junit'
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

repositories {
    mavenCentral()
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

ext {
    set('testcontainersVersion', "1.17.3")
}

// wrapper

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
    implementation 'org.liquibase:liquibase-core'
    implementation 'com.vladmihalcea:hibernate-types-55:2.17.1'
    implementation 'org.openapitools:jackson-databind-nullable:0.2.3'
    implementation 'org.modelmapper:modelmapper:3.1.0'

    compileOnly 'org.projectlombok:lombok'

    developmentOnly 'org.springframework.boot:spring-boot-devtools'

    runtimeOnly 'org.postgresql:postgresql'

    annotationProcessor 'org.projectlombok:lombok'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.testcontainers:testcontainers'
    testImplementation 'org.testcontainers:junit-jupiter'
    testImplementation 'org.testcontainers:postgresql'
    testImplementation 'com.tngtech.archunit:archunit-junit5:1.0.0-rc1'
    testImplementation 'io.rest-assured:spring-mock-mvc'
}

dependencyManagement {
    imports {
        mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
    }
}

// Java Compiler Options
tasks.withType(JavaCompile) {
    options.compilerArgs += [
            "-parameters" // keep parameter names => no need for @Param for SpringData
    ]
}

// Use JUnit Jupiter
tasks.named('test') {
    useJUnitPlatform()
}

// OpenAPI Source Code Generation
openapiProcessor {
    spring {
        processor 'io.openapiprocessor:openapi-processor-spring:2022.4'
        apiPath "$projectDir/src/main/resources/api-definition.yaml"
        targetDir "$projectDir/build/generated/sources/openapi"
        mapping "$projectDir/src/main/resources/api-mappings.yaml"
        showWarnings true
        openApiNullable true
    }
}
sourceSets.main.java.srcDir 'build/generated/sources/openapi'
project.tasks.processResources.dependsOn('processSpring')
project.tasks.compileJava.dependsOn('processSpring')

// Spotless Code Formatting
spotless {
    java {
        // removeUnusedImports() TODO: reactivate once it can deal with multi-line-strings
        indentWithSpaces(4)
        endWithNewline()
        toggleOffOn()

        target fileTree(rootDir) {
            include '**/*.java'
            exclude '**/generated/**/*.java'
        }
    }
}
project.tasks.check.dependsOn(spotlessCheck)

// OWASP Dependency Security Test
dependencyCheck {
    cveValidForHours=4
    format = 'ALL'
    suppressionFile = 'etc/owasp-dependency-check-suppression.xml'
    failOnError = true
    failBuildOnCVSS = 7
}
project.tasks.check.dependsOn(dependencyCheckAnalyze)

// License Check
licenseReport {
    excludeBoms = true
    allowedLicensesFile = new File("$projectDir/etc/allowed-licenses.json")
}
project.tasks.check.dependsOn(checkLicense)

// JaCoCo Test Code Coverage
jacoco {
    toolVersion = "0.8.8"
}
test {
    finalizedBy jacocoTestReport // generate report after tests
    excludes = [
            'net.hostsharing.hsadminng.generated.**',
    ]
}
jacocoTestReport {
    dependsOn test
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: [
                    "net/hostsharing/hsadminng/generated/**/*.class",

                    // TODO: improve test code coverage for these classes:
                    "net/hostsharing/hsadminng/rbac/rbacuser/UserController.class",
                    "net/hostsharing/hsadminng/rbac/rbacgrant/GrantController.class",
                    "net/hostsharing/hsadminng/hs/hscustomer/CustomerController.class"
            ])
        }))
    }
    doLast {
        println "HTML Jacoco Test Code Coverage Report: file://${reports.html.outputLocation.get()}/index.html"
    }
}
project.tasks.check.dependsOn(jacocoTestCoverageVerification)
jacocoTestCoverageVerification {
    violationRules {
        rule {
            excludes = ['net.hostsharing.hsadminng.generated.**']
            limit {
                minimum = 0.7 // TODO: increase to 0.9
            }
        }

        // element: PACKAGE, BUNDLE, CLASS, SOURCEFILE or METHOD
        // counter:  INSTRUCTION, BRANCH, LINE, COMPLEXITY, METHOD, or CLASS
        // value: TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO or MISSEDRATIO

        rule {
            element = 'CLASS'
            excludes = [
                    'net.hostsharing.hsadminng.generated.**',
                    'net.hostsharing.hsadminng.HsadminNgApplication',
                    'net.hostsharing.hsadminng.TestController',

                    // TODO: improve test code coverage:
                    'net.hostsharing.hsadminng.rbac.rbacuser.UserController',
                    'net.hostsharing.hsadminng.hs.hscustomer.CustomerController'
            ]

            limit {
                counter = 'LINE'
                value = 'COVEREDRATIO'
                minimum = 0.7
            }
        }
        rule {
            element = 'METHOD'
            excludes = [
                    'net.hostsharing.hsadminng.generated.**',
                    'net.hostsharing.hsadminng.HsadminNgApplication.*',

                    // TODO: improve test code coverage:
                    'net.hostsharing.hsadminng.rbac.rbacuser.RbacUserController.listUsers(*)',
                    'net.hostsharing.hsadminng.rbac.rbacuser.RbacUserController.listUserPermissions(*)',
                    'net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantController.listUserGrants(*)',
                    'net.hostsharing.hsadminng.hs.hscustomer.CustomerController.addCustomer(java.lang.String, java.lang.String, net.hostsharing.hsadminng.generated.api.v1.model.CustomerResource)'
            ]

            limit {
                counter = 'BRANCH'
                value = 'COVEREDRATIO'
                minimum = 0.5 // TODO: increase test code coverage
            }
        }
    }
}