plugins {
    // Gradle plugin portal
    alias(libs.plugins.tripletPlay)
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.parcelize)
    alias(libs.plugins.kotlin.serialization)
    alias(libs.plugins.keeper)
    id 'idea'
}

repositories {
    google()
    mavenCentral()
    maven { url = "https://jitpack.io" }
}

keeper {
    traceReferences {
        // Silence missing definitions
        arguments.set(["--map-diagnostics:MissingDefinitionsDiagnostic", "error", "none"])
    }
}

idea {
    module {
        downloadJavadoc = System.getenv("CI") != "true"
        downloadSources = System.getenv("CI") != "true"
    }
}

def homePath = System.properties['user.home']
/**
 * @return the current git hash
 * @example edf739d95bad7b370a6ed4398d46723f8219b3cd
 */
static def gitCommitHash() {
    "git rev-parse HEAD".execute().text.trim()
}

android {
    namespace = "com.ichi2.anki"

    compileSdk = libs.versions.compileSdk.get().toInteger()

    buildFeatures {
        buildConfig = true
        aidl = true
    }

    if (rootProject.testReleaseBuild) {
        testBuildType = "release"
    } else {
        testBuildType = "debug"
    }

    defaultConfig {
        applicationId = "com.ichi2.anki"
        buildConfigField "Boolean", "CI", (System.getenv("CI") == "true").toString()
        buildConfigField "String", "ACRA_URL", '"https://ankidroid.org/acra/report"'
        buildConfigField "String", "BACKEND_VERSION", "\"${libs.versions.ankiBackend.get()}\""
        buildConfigField "Boolean", "ENABLE_LEAK_CANARY", "false"
        buildConfigField "String", "GIT_COMMIT_HASH", "\"${gitCommitHash()}\""
        buildConfigField "long", "BUILD_TIME", System.currentTimeMillis().toString()
        resValue "string", "app_name", "AnkiDroid"

        // The version number is of the form:
        // <major>.<minor>.<maintenance>[dev|alpha<build>|beta<build>|]
        // The <build> is only present for alpha and beta releases (e.g., 2.0.4alpha2 or 2.0.4beta4), developer builds do
        // not have a build number (e.g., 2.0.4dev) and official releases only have three components (e.g., 2.0.4).
        //
        // The version code is derived from the version name as follows:
        // AbbCCtDD
        // A: 1-digit decimal number representing the major version
        // bb: 2-digit decimal number representing the minor version
        // CC: 2-digit decimal number representing the maintenance version
        // t: 1-digit decimal number representing the type of the build
        // 0: developer build
        // 1: alpha release
        // 2: beta release
        // 3: public release
        // DD: 2-digit decimal number representing the build
        // 00 for internal builds and public releases
        // alpha/beta build number for alpha/beta releases
        //
        // This ensures the correct ordering between the various types of releases (dev < alpha < beta < release) which is
        // needed for upgrades to be offered correctly.
        versionCode=22300104
        versionName="2.23.0alpha4"
        minSdk = libs.versions.minSdk.get().toInteger()

        // After #13695: change .tests_emulator.yml
        targetSdk = libs.versions.targetSdk.get().toInteger()
        testApplicationId = "com.ichi2.anki.tests"
        vectorDrawables.useSupportLibrary = true
        testInstrumentationRunner = 'com.ichi2.testutils.NewCollectionPathTestRunner'
    }
    signingConfigs {
        release {
            def keystorePath = System.getenv("KEYSTOREPATH")
            if (keystorePath != null && !keystorePath.trim().isEmpty()) {
                storeFile file(keystorePath)
                storePassword System.getenv("KEYSTOREPWD") ?: System.getenv("KSTOREPWD")
                keyAlias System.getenv("KEYALIAS")
                keyPassword System.getenv("KEYPWD")
            } else {
                storeFile file("${rootDir}/tools/fallback-release-keystore.jks")
                storePassword "Test@123"
                keyAlias "my-key"
                keyPassword "Test@123"
            }
        }
    }
    buildTypes {
        named('debug') {
            versionNameSuffix "-debug"
            debuggable true
            applicationIdSuffix ".debug"
            splits.abi.universalApk = true // Build universal APK for debug always
            // Check Crash Reports page on developer wiki for info on ACRA testing
            // buildConfigField "String", "ACRA_URL", '"https://918f7f55-f238-436c-b34f-c8b5f1331fe5-bluemix.cloudant.com/acra-ankidroid/_design/acra-storage/_update/report"'
            if (project.rootProject.file('local.properties').exists()) {
                Properties localProperties = new Properties()
                localProperties.load(project.rootProject.file('local.properties').newDataInputStream())
                // #6009 Allow optional disabling of JaCoCo for general build (assembleDebug).
                // jacocoDebug task was slow, hung, and wasn't required unless I wanted coverage
                testCoverageEnabled = localProperties['enable_coverage'] != "false"
                // not profiled: optimization for build times
                if (localProperties['enable_languages'] == "false") {
                    android.defaultConfig.resConfigs "en"
                }
                // allow disabling leak canary
                if (localProperties["enable_leak_canary"] != null) {
                    buildConfigField "Boolean", "ENABLE_LEAK_CANARY", localProperties["enable_leak_canary"]
                } else {
                    buildConfigField "Boolean", "ENABLE_LEAK_CANARY", "true"
                }
            } else {
                testCoverageEnabled true
            }

            // make the icon red if in debug mode
            resValue 'color', 'anki_foreground_icon_color_0', "#FFFF0000"
            resValue 'color', 'anki_foreground_icon_color_1', "#FFFF0000"
            resValue "string", "applicationId", "${defaultConfig.applicationId}${applicationIdSuffix}"
        }
        named('release') {
            testCoverageEnabled = testReleaseBuild
            minifyEnabled System.getenv("MINIFY_ENABLED") ? System.getenv("MINIFY_ENABLED") != "false" : true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            testProguardFile 'proguard-test-rules.pro'
            splits.abi.universalApk = universalApkEnabled // Build universal APK for release with `-Duniversal-apk=true`
            signingConfig = signingConfigs.release

            // syntax: assembleRelease -PcustomSuffix="suffix" -PcustomName="New name"
            if (project.hasProperty("customSuffix")) {
                // the suffix needs a '.' at the start
                applicationIdSuffix project.property("customSuffix").replaceFirst(/^\.*/, ".")
                resValue "string", "applicationId", "${defaultConfig.applicationId}${applicationIdSuffix}"
            } else {
                resValue "string", "applicationId", defaultConfig.applicationId
            }
            if (project.hasProperty("customName")) {
                resValue "string", "app_name", project.property("customName")
            }

            resValue 'color', 'anki_foreground_icon_color_0', "#FF29B6F6"
            resValue 'color', 'anki_foreground_icon_color_1', "#FF0288D1"
        }
    }

    /**
     * Product Flavors are used for Amazon App Store and Google Play Store.
     * This is because we cannot use Camera Permissions in Amazon App Store (for FireTv etc...)
     * Therefore, different AndroidManifest for Camera Permissions is used in Amazon flavor.
     *
     * This flavor block must stay in sync with the same block in testlib/build.gradle.kts
     */
    flavorDimensions += "appStore"
    productFlavors {
        create('play') {
            getIsDefault().set(true)
            dimension "appStore"
        }
        create('amazon') {
            dimension "appStore"
        }
        // A 'full' build has no restrictions on storage/camera. Distributed on GitHub/F-Droid
        create('full') {
            dimension "appStore"
        }
    }

    /**
     * Set this to true to create five separate APKs instead of one:
     *   - 2 APKs that only work on ARM/ARM64 devices
     *   - 2 APKs that only works on x86/x86_64 devices
     *   - a universal APK that works on all devices
     * The advantage is the size of most APKs is reduced by about 2.5MB.
     * Upload all the APKs to the Play Store and people will download
     * the correct one based on the CPU architecture of their device.
     */
    def enableSeparateBuildPerCPUArchitecture = true

    splits {
        abi {
            reset()
            enable = enableSeparateBuildPerCPUArchitecture
            //universalApk enableUniversalApk  // set in debug + release config blocks above
            include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
        }
    }
    // applicationVariants are e.g. debug, release
    applicationVariants.configureEach { variant ->
        // We want the same version stream for all ABIs in debug but for release we can split them
        if (variant.buildType.name == 'release') {
            variant.outputs.configureEach { output ->

                // For each separate APK per architecture, set a unique version code as described here:
                // https://developer.android.com/studio/build/configure-apk-splits.html
                def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
                def outputFile = output.outputFile
                if (outputFile != null && outputFile.name.endsWith('.apk')) {
                    def abi = output.getFilter("ABI")
                    if (abi != null) {  // null for the universal-debug, universal-release variants
                        //  From: https://developer.android.com/studio/publish/versioning#appversioning
                        //  "Warning: The greatest value Google Play allows for versionCode is 2100000000"
                        //  AnkiDroid versionCodes have a budget 8 digits (through AnkiDroid 9)
                        //  This style does ABI version code ranges with the 9th digit as 0-4.
                        //  This consumes ~20% of the version range space, w/50 years of versioning at our major-version pace
                        output.versionCodeOverride =
                                // ex:  321200106 = 3 * 100000000 + 21200106
                                versionCodes.get(abi) * 100000000 + defaultConfig.versionCode
                    }
                }
            }
        }
    }

    testOptions {
        animationsDisabled = true
        kotlin {
            compilerOptions {
                freeCompilerArgs.add("-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi")
            }
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
        coreLibraryDesugaringEnabled = true
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_11
    }

    packagingOptions {
        resources {
            excludes += ['META-INF/DEPENDENCIES']
        }
    }
}

play {
    serviceAccountCredentials.set(file("${homePath}/src/AnkiDroid-GCP-Publish-Credentials.json"))
    track.set('alpha')

    // any time we bump minSdk we want Play Store to retain the old artifacts by version code,
    // so that they remain available for older devices
    retain {
        artifacts.set([
                20700300L, // (2.7, minSdk 10, universal APK)
                20804300L, // (2.8.4, minSdk 10, universal APK)
                21004300L, // (2.10.4, minSdk 15, universal APK)
                // release-2.14 minSdk 16: missing and not re-publishable, see issue 17791
                121603300L, // (2.16.3, minSdk 21, ABI armeabi-v7a)
                221603300L, // (2.16.3, minSdk 21, ABI x86)
                321603300L, // (2.16.3, minSdk 21, ABI arm64-v8a)
                421603300L, // (2.16.3, minSdk 21, ABI x86_64)
                121905300L, // (2.19.5, minSdk 23, ABI armeabi-v7a)
                221905300L, // (2.19.5, minSdk 23, ABI x86)
                321905300L, // (2.19.5, minSdk 23, ABI arm64-v8a)
                421905300L, // (2.19.5, minSdk 23, ABI x86_64)
        ])
    }

    // If you retain APKs in a release with different names as we do above,
    // the plugin + Play Store has no idea how to name the release except by date.
    // release name is developer only, but sane names really help, so set one
    releaseName.set(android.defaultConfig.versionName)
}

// Install Git pre-commit hook for Ktlint
tasks.register('installGitHook', Copy) {
    from new File(rootProject.rootDir, 'pre-commit')
    into { new File(rootProject.rootDir, '.git/hooks') }
    filePermissions {
        user {
            read = write = execute = true
        }
    }
}
// to run manually: `./gradlew installGitHook`
tasks.named('preBuild').configure { dependsOn('installGitHook') }

tasks.register('copyTestLibIntoAndroidTest', Copy) {
    into new File(rootProject.rootDir, 'AnkiDroid/src/androidTest/java/com/ichi2/testutils')
    from new File(rootProject.rootDir, 'testlib/src/main/java/com/ichi2/testutils')
    into ('common') {
        from 'common'
    }
}
tasks.named('preBuild').configure { dependsOn('copyTestLibIntoAndroidTest') }
tasks.named('runKtlintCheckOverAndroidTestSourceSet').configure { mustRunAfter('copyTestLibIntoAndroidTest') }

// Issue 11078 - some emulators run, but run zero tests, and still report success
tasks.register('assertNonzeroAndroidTests') {
    doLast {
        // androidTest currently creates one .xml file per emulator with aggregate results in this dir
        File folder = file("./build/outputs/androidTest-results/connected/flavors/play")
        File[] listOfFiles = folder.listFiles({ d, f -> f ==~ /.*.xml/ } as FilenameFilter)
        for (File file : listOfFiles) {
            // The aggregate results file currently contains a line with this pattern holding test count
            String[] matches = file.readLines().findAll { it.contains('<testsuite') }
            if (matches.length != 1) {
                throw new GradleScriptException("Unable to determine count of tests executed for " + file.name + ". Regex pattern out of date?", null)
            }
            if (!(matches[0] ==~ /.* tests="\d+" .*/) || matches[0].contains('tests="0"')) {
                throw new GradleScriptException("androidTest executed 0 tests for " + file.name + " - Probably a bug with the emulator. Try another image.", null)
            }
        }
    }
}
afterEvaluate {
    tasks.named("connectedPlay${rootProject.androidTestVariantName}AndroidTest").configure {
        finalizedBy('assertNonzeroAndroidTests')
    }
    tasks.named("packagePlay${rootProject.androidTestVariantName}AndroidTest").configure {
        // Issue 17520 - ensures testlib is built
        dependsOn(":testlib:assemblePlay${rootProject.androidTestVariantName}")
    }
}

apply from: "./robolectricDownloader.gradle"
apply from: "./jacoco.gradle"
apply from: "../lint.gradle"

dependencies {
    configurations.configureEach {
        resolutionStrategy {
            // Timber has this as a dependency but they are not up to date. We want to force our version.
            force libs.jetbrains.annotations
        }
    }
    api project(":api")
    implementation libs.androidx.work.runtime
    lintChecks project(":lint-rules")
    coreLibraryDesugaring libs.desugar.jdk.libs.nio

    compileOnly libs.jetbrains.annotations
    compileOnly libs.auto.service.annotations
    annotationProcessor libs.auto.service

    // modules
    implementation project(":common")
    implementation project(":libanki")

    implementation libs.androidx.activity
    implementation libs.androidx.annotation
    implementation libs.androidx.appcompat
    implementation libs.androidx.browser
    implementation libs.androidx.core.ktx
    implementation libs.androidx.draganddrop
    implementation libs.androidx.exifinterface
    implementation libs.androidx.fragment.ktx
    implementation libs.androidx.media
    implementation libs.androidx.preference.ktx
    implementation libs.androidx.recyclerview
    implementation libs.androidx.sqlite.framework
    implementation libs.androidx.swiperefreshlayout
    implementation libs.androidx.viewpager2
    implementation libs.androidx.constraintlayout
    implementation libs.androidx.webkit
    implementation libs.google.material
    implementation libs.android.image.cropper
    implementation libs.nanohttpd
    implementation libs.kotlinx.serialization.json
    implementation libs.seismic

    debugImplementation libs.androidx.fragment.testing.manifest

    // Backend libraries

    implementation libs.protobuf.kotlin.lite // This is required when loading from a file

    Properties localProperties = new Properties()
    if (project.rootProject.file('local.properties').exists()) {
        localProperties.load(project.rootProject.file('local.properties').newDataInputStream())
    }
    if (localProperties['local_backend'] == "true") {
        implementation files("../../Anki-Android-Backend/rsdroid/build/outputs/aar/rsdroid-release.aar")
        testImplementation files("../../Anki-Android-Backend/rsdroid-testing/build/libs/rsdroid-testing.jar")
    } else {
        implementation libs.ankiBackend.backend
        testImplementation libs.ankiBackend.testing
    }

    // May need a resolution strategy for support libs to our versions
    implementation libs.acra.limiter
    implementation libs.acra.toast
    implementation libs.acra.dialog
    implementation libs.acra.http

    implementation libs.commons.compress
    implementation libs.commons.collections4 // SetUniqueList
    implementation libs.commons.io // FileUtils.contentEquals
    implementation libs.mikehardy.google.analytics.java7
    implementation libs.okhttp
    implementation libs.slf4j.timber
    implementation libs.jakewharton.timber
    implementation libs.jsoup
    implementation libs.java.semver // For AnkiDroid JS API Versioning
    implementation libs.drakeet.drawer
    implementation libs.tapTargetPrompt
    implementation libs.colorpicker
    implementation libs.kotlin.reflect
    implementation libs.kotlin.test
    implementation libs.search.preference

    // Cannot use debugImplementation since classes need to be imported in AnkiDroidApp
    // and there's no no-op version for release build. Usage has been disabled for release
    // build via AnkiDroidApp.
    implementation libs.leakcanary.android

    testImplementation project(':testlib')
    testImplementation project(":libanki:testutils")

    // A path for a testing library which provide Parameterized Test
    testImplementation libs.junit.jupiter
    testImplementation libs.junit.jupiter.params
    testImplementation libs.junit.vintage.engine
    testImplementation libs.mockito.inline
    // only needed until mockito-kotlin bumps transitive core dep to
    // a version that supports JDK24 (and eventually 25LTS)
    testImplementation libs.mockito.core
    testImplementation(libs.mockito.kotlin) {
        // only needed until mockito-kotlin bumps transitive core dep to
        // a version that supports JDK24 (and eventually 25LTS)
        exclude module: "mockitoCore"
    }
    testImplementation libs.hamcrest
    // robolectricDownloader.gradle *may* need a new SDK jar entry if they release one or if we change targetSdk. Instructions in that gradle file.
    testImplementation libs.robolectric
    testImplementation libs.androidx.test.core
    testImplementation libs.androidx.test.junit
    testImplementation libs.kotlin.reflect
    testImplementation libs.kotlin.test
    testImplementation libs.kotlin.test.junit5
    testImplementation libs.kotlinx.coroutines.test
    testImplementation libs.mockk
    testImplementation libs.androidx.fragment.testing
    // in a JvmTest we need org.json.JSONObject to not be mocked
    testImplementation libs.json
    testImplementation libs.ivanshafran.shared.preferences.mock
    testImplementation libs.androidx.test.runner
    testImplementation libs.androidx.test.rules
    testImplementation libs.androidx.espresso.core
    testImplementation(libs.androidx.espresso.contrib) {
        exclude module: "protobuf-lite"
    }
    testImplementation libs.androidx.work.testing
    // for testing flows
    testImplementation libs.cashapp.turbine

    // we should depend directly on the common testlib for androidTest, but we cannot
    // until coverage-breaking issue is fixed https://issuetracker.google.com/issues/332746900
    // androidTestImplementation project(':testlib')

    // May need a resolution strategy for support libs to our versions
    androidTestImplementation libs.androidx.espresso.core
    androidTestImplementation(libs.androidx.espresso.contrib) {
        exclude module: "protobuf-lite"
    }
    androidTestImplementation libs.androidx.test.core
    androidTestImplementation libs.androidx.test.junit
    androidTestImplementation libs.androidx.test.rules
    androidTestImplementation libs.androidx.uiautomator
    androidTestImplementation libs.kotlin.test
    androidTestImplementation libs.kotlin.test.junit
    androidTestImplementation libs.androidx.fragment.testing

    implementation libs.androidx.media3.exoplayer
    implementation libs.androidx.media3.exoplayer.dash
    implementation libs.androidx.media3.ui
}
