/*
 * Nextcloud - Android Client
 *
 * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
 * SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
 * SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
 * SPDX-FileCopyrightText: 2024 Andy Scherzinger <info@andy-scherzinger.de>
 * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas <alvaro@alvarobrey.com>
 * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
 */
import com.github.spotbugs.snom.Confidence
import com.github.spotbugs.snom.Effort
import com.github.spotbugs.snom.SpotBugsTask
import org.gradle.internal.jvm.Jvm

buildscript {
    dependencies {
        classpath "com.android.tools.build:gradle:$androidPluginVersion"
        classpath libs.spotbugs.gradle.plugin
        classpath libs.kotlin.gradle.plugin
        classpath libs.detekt.gradle.plugin
        classpath libs.commons.httpclient.commons.httpclient // remove after entire switch to lib v2
        classpath libs.shot
        classpath "org.jacoco:org.jacoco.core:$jacoco_version"
        classpath "org.jacoco:org.jacoco.report:$jacoco_version"
        classpath "org.jacoco:org.jacoco.agent:$jacoco_version"
    }
}

plugins {
    alias(libs.plugins.kotlin.compose)
    alias(libs.plugins.spotless)
    alias(libs.plugins.kapt)
    alias(libs.plugins.ksp) apply false
}

apply plugin: "com.android.application"

apply plugin: "kotlin-android"
apply plugin: "kotlin-parcelize"
apply plugin: "checkstyle"
apply plugin: "pmd"
apply from: "$rootProject.projectDir/jacoco.gradle"
apply plugin: "com.github.spotbugs"
apply plugin: "io.gitlab.arturbosch.detekt"

// needed to make renovate run without shot, as shot requires Android SDK
// https://github.com/pedrovgs/Shot/issues/300
if (shotTest) {
    apply plugin: "shot"
}
apply plugin: "com.google.devtools.ksp"


println "Gradle uses Java ${Jvm.current()}"

configurations {
    configureEach {
        exclude group: "org.jetbrains", module: "annotations-java5" // via prism4j, already using annotations explicitly
    }
}


configurations.configureEach {
    resolutionStrategy.eachDependency {
        if (requested.group == "org.checkerframework" && requested.name != "checker-compat-qual") {
            useVersion(checkerVersion)
            because("https://github.com/google/ExoPlayer/issues/10007")
        }

        if (requested.group == "commons-logging" && requested.name == "commons-logging") {
            useTarget("org.slf4j:jcl-over-slf4j:1.7.4")
        }
    }
}

// semantic versioning for version code
def versionMajor = 3
def versionMinor = 34
def versionPatch = 0
def versionBuild = 90 // 0-50=Alpha / 51-98=RC / 90-99=stable


def ndkEnv = new HashMap<String, String>()
file("$project.rootDir/ndk.env").readLines().each() {
    def (key, value) = it.tokenize("=")
    ndkEnv.put(key, value)
}

def perfAnalysis = project.hasProperty("perfAnalysis")

def getConfigProperties() {
    def props = new Properties()
    def file = rootProject.file(".gradle/config.properties")
    if (file.exists()) {
        props.load(new FileInputStream(file))
    }
    return props
}

def configProps = getConfigProperties()

android {
    // install this NDK version and Cmake to produce smaller APKs. Build will still work if not installed
    ndkVersion = "${ndkEnv.get("NDK_VERSION")}"


    namespace = "com.owncloud.android"
    testNamespace = "${namespace}.test"

    androidResources {
        generateLocaleConfig = true
    }

    defaultConfig {
        applicationId = "com.nextcloud.client"
        minSdk = 27
        targetSdk = 35
        compileSdk = 35

        buildConfigField "boolean", "CI", ciBuild.toString()
        buildConfigField "boolean", "RUNTIME_PERF_ANALYSIS", perfAnalysis.toString()

        javaCompileOptions {
            annotationProcessorOptions {
                arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }

        // arguments to be passed to functional tests
        if (shotTest) {
            testInstrumentationRunner "com.karumi.shot.ShotTestRunner"
        } else {
            testInstrumentationRunner "com.nextcloud.client.TestRunner"
        }
        testInstrumentationRunnerArgument "TEST_SERVER_URL", "${NC_TEST_SERVER_BASEURL}"
        testInstrumentationRunnerArgument "TEST_SERVER_USERNAME", "${NC_TEST_SERVER_USERNAME}"
        testInstrumentationRunnerArgument "TEST_SERVER_PASSWORD", "${NC_TEST_SERVER_PASSWORD}"
        testInstrumentationRunnerArguments disableAnalytics: "true"

        versionCode versionMajor * 10000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild

        if (versionBuild > 89) {
            versionName "${versionMajor}.${versionMinor}.${versionPatch}"
        } else if (versionBuild > 50) {
            versionName "${versionMajor}.${versionMinor}.${versionPatch} RC" + (versionBuild - 50)
        } else {
            versionName "${versionMajor}.${versionMinor}.${versionPatch} Alpha" + (versionBuild + 1)
        }

        // adapt structure from Eclipse to Gradle/Android Studio expectations;
        // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure

        flavorDimensions += "default"

        buildTypes {
            release {
                buildConfigField "String", "NC_TEST_SERVER_DATA_STRING", "\"\""
            }

            debug {
                testCoverageEnabled = project.hasProperty("coverage")
                resConfigs "xxxhdpi"

                buildConfigField "String", "NC_TEST_SERVER_DATA_STRING", "\"nc://login/user:${configProps['NC_TEST_SERVER_USERNAME']}&password:${configProps['NC_TEST_SERVER_PASSWORD']}&server:${configProps['NC_TEST_SERVER_BASEURL']}\""
            }
        }

        buildFeatures {
            buildConfig = true
        }

        productFlavors {
            // used for f-droid
            generic {
                applicationId "com.nextcloud.client"
                dimension "default"
            }

            gplay {
                applicationId "com.nextcloud.client"
                dimension "default"
            }

            huawei {
                applicationId "com.nextcloud.client"
                dimension "default"
            }

            versionDev {
                applicationId "com.nextcloud.android.beta"
                dimension "default"
                versionCode 20220322
                versionName "20220322"
            }

            qa {
                applicationId "com.nextcloud.android.qa"
                dimension "default"
                versionCode 1
                versionName "1"
            }
        }

        testOptions {
            unitTests.returnDefaultValues = true
            animationsDisabled = true
        }
    }

    // adapt structure from Eclipse to Gradle/Android Studio expectations;
    // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure
    packagingOptions {
        resources {
            excludes += "META-INF/LICENSE*"
            excludes += "META-INF/versions/9/OSGI-INF/MANIFEST*"
            pickFirst "MANIFEST.MF" // workaround for duplicated manifest on some dependencies
        }
    }

    tasks.register("checkstyle", Checkstyle) {
        configFile = file("${rootProject.projectDir}/checkstyle.xml")
        configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath
        source "src"
        include "**/*.java"
        exclude "**/gen/**"
        classpath = files()
    }

    tasks.register("pmd", Pmd) {
        ruleSetFiles = files("${project.rootDir}/ruleset.xml")
        ignoreFailures = true // should continue checking
        ruleSets = []

        source "src"
        include "**/*.java"
        exclude "**/gen/**"

        reports {
            xml {
                destination = layout.buildDirectory.file("reports/pmd/pmd.xml").get().asFile
            }
            html {
                destination = layout.buildDirectory.file("reports/pmd/pmd.html").get().asFile
            }
        }
    }

    check.dependsOn "checkstyle", "spotbugsGplayDebug", "pmd", "lint", "spotlessKotlinCheck", "detekt"

    buildFeatures {
        dataBinding = true
        viewBinding = true
        aidl = true
        compose = true
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }

    kotlinOptions {
        jvmTarget = "17"
    }

    lint {
        abortOnError = false
        checkGeneratedSources = true
        disable "MissingTranslation", "GradleDependency", "VectorPath", "IconMissingDensityFolder", "IconDensities", "GoogleAppIndexingWarning", "MissingDefaultResource", "InvalidPeriodicWorkRequestInterval", "StringFormatInvalid", "MissingQuantity"
        htmlOutput = layout.buildDirectory.file("reports/lint/lint.html").get().asFile
        htmlReport = true
    }

    sourceSets {
        // Adds exported schema location as test app assets.
        androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
    }

    kapt {
        useBuildCache = true
    }
}

dependencies {
    // region Nextcloud library
    implementation("com.github.nextcloud:android-library:$androidLibraryVersion") {
        exclude group: "org.ogce", module: "xpp3" // unused in Android and brings wrong Junit version
    }
    // endregion

    // region Splash Screen
    implementation libs.splashscreen
    // endregion

    // region Jetpack Compose
    implementation(platform(libs.compose.bom))
    implementation(libs.compose.ui)
    implementation(libs.compose.ui.graphics)
    implementation(libs.compose.material3)
    debugImplementation(libs.compose.ui.tooling)
    implementation(libs.compose.ui.tooling.preview)
    // endregion

    // region Media3
    implementation libs.media3.ui
    implementation libs.media3.session
    implementation libs.media3.exoplayer
    implementation libs.media3.datasource
    // endregion

    // region Room
    implementation libs.room.runtime
    ksp "androidx.room:room-compiler:$roomVersion"
    androidTestImplementation libs.room.testing
    // endregion

    // region Espresso
    androidTestImplementation libs.espresso.core
    androidTestImplementation libs.espresso.contrib
    androidTestImplementation libs.espresso.web
    androidTestImplementation libs.espresso.accessibility
    androidTestImplementation libs.espresso.intents
    androidTestImplementation libs.espresso.idling.resource
    // endregion

    // region Glide
    implementation libs.glide
    ksp libs.ksp
    // endregion

    // region UI
    implementation libs.appcompat
    implementation libs.webkit
    implementation libs.cardview
    implementation libs.exifinterface
    implementation libs.fragment.ktx
    // endregion

    // region Worker
    implementation libs.work.runtime
    implementation libs.work.runtime.ktx
    // endregion

    // region Lifecycle
    implementation libs.lifecycle.viewmodel.ktx
    implementation libs.lifecycle.service
    implementation(libs.lifecycle.runtime.ktx)
    // endregion

    // region JUnit
    androidTestImplementation libs.junit
    androidTestImplementation libs.rules
    androidTestImplementation libs.runner
    androidTestUtil libs.orchestrator
    androidTestImplementation libs.core.ktx
    androidTestImplementation libs.core.testing
    // endregion

    // region other libraries
    compileOnly libs.org.jbundle.util.osgi.wrapped.org.apache.http.client
    implementation libs.commons.httpclient.commons.httpclient // remove after entire switch to lib v2
    implementation libs.jackrabbit.webdav // remove after entire switch to lib v2
    implementation libs.constraintlayout
    implementation libs.legacy.support.v4
    implementation libs.material
    implementation libs.disklrucache
    implementation libs.juniversalchardet // need this version for Android <7
    compileOnly libs.annotations
    implementation libs.commons.io
    implementation libs.eventbus
    implementation libs.ez.vcard
    implementation libs.nnio
    implementation libs.bcpkix.jdk18on
    implementation libs.gson
    implementation libs.sectioned.recyclerview
    implementation libs.photoview
    implementation libs.android.gif.drawable
    implementation libs.qrcodescanner // "com.github.blikoon:QRCodeScanner:0.1.2"
    implementation libs.flexbox
    implementation libs.androidsvg
    implementation libs.annotation
    implementation libs.emoji.google
    // endregion

    // region AppScan, document scanner not available on FDroid (generic) due to OpenCV binaries
    gplayImplementation project(":appscan")
    huaweiImplementation project(":appscan")
    qaImplementation project(":appscan")
    // endregion

    // region SpotBugs
    spotbugsPlugins libs.findsecbugs.plugin
    spotbugsPlugins libs.fb.contrib
    // endregion

    // region Dagger
    implementation libs.dagger
    implementation libs.dagger.android
    implementation libs.dagger.android.support
    kapt "com.google.dagger:dagger-compiler:$daggerVersion"
    kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
    // endregion

    // region Crypto
    implementation libs.conscrypt.android
    // endregion

    // region Library
    implementation libs.library
    // endregion

    // region Shimmer
    implementation libs.loaderviewlibrary
    // endregion

    // region Markdown rendering
    implementation libs.core
    implementation libs.ext.strikethrough
    implementation libs.ext.tables
    implementation libs.ext.tasklist
    implementation libs.html
    implementation libs.syntax.highlight
    implementation libs.prism4j
    kapt "io.noties:prism4j-bundler:$prismVersion"
    // endregion

    // region Image cropping / rotation
    implementation libs.android.image.cropper
    // endregion

    // region Maps
    implementation libs.osmdroid.android
    // endregion

    // region iCal4j
    implementation(libs.ical4j) {
        ["org.apache.commons", "commons-logging"].each {
            exclude group: "$it"
        }
    }
    // endregion

    // region LeakCanary
    if (perfAnalysis) {
        debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
    }
    // endregion

    // region Local Unit Test
    testImplementation libs.junit.junit
    testImplementation libs.mockito.core
    testImplementation libs.test.core
    testImplementation libs.json
    testImplementation libs.mockito.kotlin
    testImplementation libs.core.testing
    testImplementation "io.mockk:mockk:$mockkVersion"
    testImplementation libs.mockk.android
    // endregion

    // region Mocking support
    androidTestImplementation libs.dexopener // required to allow mocking on API 27 and older
    androidTestImplementation libs.mockito.kotlin
    androidTestImplementation libs.mockito.core
    androidTestImplementation(libs.mockito.android)
    androidTestImplementation libs.mockk.android
    androidTestImplementation libs.screenshot.core
    // endregion

    // region UIAutomator
    // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests
    // androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
    // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details
    // androidTestImplementation "com.android.support:support-annotations:${supportLibraryVersion}"
    androidTestImplementation libs.screengrab
    // endregion

    // region Kotlin
    implementation libs.kotlin.stdlib
    // endregion

    // region Stateless
    implementation libs.stateless4j
    // endregion

    // region Google Play dependencies, upon each update first test: new registration, receive push
    gplayImplementation libs.firebase.messaging
    gplayImplementation libs.play.services.base
    gplayImplementation libs.review.ktx
    // endregion

    // region UI
    implementation libs.ui
    // endregion

    // region Image loading
    implementation libs.coil
    // endregion
}


configurations.configureEach {
    resolutionStrategy {
        force "org.objenesis:objenesis:3.4"
        eachDependency { details ->
            if ("org.jacoco" == details.requested.group) {
                details.useVersion "$jacoco_version"
            }
        }
    }
}

// Run the compiler as a separate process
tasks.withType(JavaCompile).configureEach {
    options.fork = true

    // Enable Incremental Compilation
    options.incremental = true
}

tasks.withType(Test).configureEach {
    // Run tests in parallel
    maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1

    // increased logging for tests
    testLogging {
        events "passed", "skipped", "failed"
    }
}

android.applicationVariants.configureEach { variant ->
    variant.outputs.configureEach { output -> outputFileName = "${output.baseName}-${variant.versionCode}.apk"
    }
}

spotless {
    kotlin {
        target "**/*.kt"
        ktlint()
    }
}

detekt {
    config.setFrom("detekt.yml")
}

if (shotTest) {
    shot {
        showOnlyFailingTestsInReports = ciBuild
        // CI environment renders some shadows slightly different from local VMs
        // Add a 0.5% tolerance to account for that
        tolerance = ciBuild ? 0.1 : 0
    }
}

jacoco {
    toolVersion = "$jacoco_version"
}

spotbugs {
    ignoreFailures = true // should continue checking
    effort = Effort.MAX
    reportLevel = Confidence.valueOf("MEDIUM")
}

tasks.withType(SpotBugsTask){task ->
    String variantNameCap = task.name.replace("spotbugs", "")
    String variantName = variantNameCap.substring(0, 1).toLowerCase() + variantNameCap.substring(1)
    dependsOn "compile${variantNameCap}Sources"

    classes = fileTree(layout.buildDirectory.get().asFile.toString()+"/intermediates/javac/${variantName}/compile${variantNameCap}JavaWithJavac/classes/")
    excludeFilter = file("${project.rootDir}/scripts/analysis/spotbugs-filter.xml")
    reports {
        xml {
            required = true
        }
        html {
            required = true
            outputLocation = layout.buildDirectory.file("reports/spotbugs/spotbugs.html").get().asFile
            stylesheet = "fancy.xsl"
        }
    }
}

ksp {
    arg("room.schemaLocation", "$projectDir/schemas")
}
