buildscript {
    def androidDir = "${buildscript.sourceFile.getParent()}/../"
    apply(from: "${androidDir}/test-app-util.gradle")
    apply(from: "${androidDir}/dependencies.gradle")
}

plugins {
    id("com.android.application")
    id("com.google.devtools.ksp").version("${kspVersion}")
    id("org.jetbrains.kotlin.android")
}

def reactNativePath = file(findNodeModulesPath("react-native", rootDir))

if (autodetectReactNativeVersion || enableNewArchitecture) {
    apply(plugin: "com.facebook.react")

    react {
        reactNativeDir = reactNativePath
        codegenDir = file(
            findNodeModulesPath("@react-native/codegen", reactNativePath)  // >= 0.72
                ?: findNodeModulesPath("react-native-codegen", reactNativePath))  // < 0.72
    }

    // We don't want the React plugin to bundle.
    tasks.whenTaskAdded { task ->
        // The task name can be found in `react-native-gradle-plugin`:
        // https://github.com/facebook/react-native/blob/0.71-stable/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt#L54
        if (task.name.startsWith("createBundle") && task.name.endsWith("JsAndAssets")) {
            task.enabled = false
        }
    }
}

// `react-native run-android` is hard-coded to look for the output APK at a very
// specific location. See
// https://github.com/react-native-community/cli/blob/6cf12b00c02aca6d4bc843446394331d71a9749e/packages/platform-android/src/commands/runAndroid/index.ts#L180
buildDir = "${rootDir}/${name}/build"

repositories {
    maven {
        url = uri("${reactNativePath}/android")
    }

    mavenCentral()
    google()

    // TODO: Remove this block when we drop support for 0.64.
    if (reactNativeVersion > 0 && reactNativeVersion < 6500) {
        // Artifacts for 0.65+ are published to Maven Central. If we're on an
        // older version, we still need to use JCenter.
        // noinspection JcenterRepositoryObsolete
        jcenter() {
            content {
                includeGroup("com.facebook.fbjni")
                includeGroup("com.facebook.flipper")
                includeGroup("com.facebook.fresco")
                includeGroup("com.facebook.yoga")
            }
        }
    }

    // https://github.com/AzureAD/microsoft-authentication-library-for-android#step-1-declare-dependency-on-msal
    maven {
        url = uri("https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1")
    }
}

apply(from: "${projectDir}/../../test-app.gradle")
applyTestAppModule(project)

project.ext.react = [
    abiSplit             : false,
    appName              : getAppName(),
    applicationId        : getApplicationId(),
    architectures        : getArchitectures(),
    bundleInRelease      : false,
    enableCamera         : !getSingleAppMode(),
    enableFabric         : isFabricEnabled(project),
    enableFlipper        : getFlipperVersion(rootDir),
    enableHermes         : true,
    enableNewArchitecture: enableNewArchitecture,
]

project.ext.signingConfigs = getSigningConfigs()

android {
    namespace "com.microsoft.reacttestapp"

    compileSdkVersion project.ext.compileSdkVersion

    if (project.hasProperty("ndkVersion")) {
        ndkVersion project.ext.ndkVersion
    }

    if (usePrefabs) {
        buildFeatures {
            prefab true
        }
    }

    // TODO: Remove this block when minSdkVersion >= 24. See
    // https://stackoverflow.com/q/53402639 for details.
    if (reactNativeVersion > 0 && reactNativeVersion < 6900) {
        compileOptions {
            sourceCompatibility(JavaVersion.VERSION_1_8)
            targetCompatibility(JavaVersion.VERSION_1_8)
        }
    }

    kotlinOptions {
        allWarningsAsErrors = true
        if (reactNativeVersion > 0) {
            if (reactNativeVersion < 6900) {
                jvmTarget = JavaVersion.VERSION_1_8
            } else if (reactNativeVersion < 7300) {
                jvmTarget = JavaVersion.VERSION_11
            }
            // else let `@react-native/gradle-plugin` handle this
        }
    }

    defaultConfig {
        applicationId project.ext.react.applicationId
        minSdkVersion project.ext.minSdkVersion
        targetSdkVersion project.ext.targetSdkVersion
        versionCode project.ext.getVersionCode()
        versionName project.ext.getVersionName()

        def (appManifest, checksum) = validateManifest(rootDir)
        buildConfigField "String", "ReactTestApp_appManifest", "\"${appManifest}\""
        buildConfigField "String", "ReactTestApp_appManifestChecksum", "\"${checksum}\""

        def recommendedFlipperVersion = getFlipperRecommendedVersion(rootDir)
        buildConfigField "String",
                         "ReactTestApp_recommendedFlipperVersion",
                         recommendedFlipperVersion ? "\"${recommendedFlipperVersion}\"" : "\"0\""

        def singleApp = getSingleAppMode()
        buildConfigField "String", "ReactTestApp_singleApp", singleApp ? "\"${singleApp}\"" : "null"

        buildConfigField "boolean", "ReactTestApp_useFabric", project.ext.react.enableFabric.toString()

        resValue "string", "app_name", project.ext.react.appName

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        if (enableNewArchitecture) {
            externalNativeBuild {
                if (reactNativeVersion > 0 && reactNativeVersion < 7000) {
                    ndkBuild {
                        arguments "APP_PLATFORM=android-${project.ext.minSdkVersion}",
                                  "APP_STL=c++_shared",
                                  "NDK_TOOLCHAIN_VERSION=clang",
                                  "GENERATED_SRC_DIR=${buildDir}/generated/source",
                                  "NODE_MODULES_DIR=${reactNativePath}/..",
                                  "PROJECT_BUILD_DIR=${buildDir}",
                                  "REACT_ANDROID_DIR=${reactNativePath}/ReactAndroid",
                                  "REACT_ANDROID_BUILD_DIR=${reactNativePath}/ReactAndroid/build"
                        cFlags "-Wall", "-Werror", "-frtti", "-fexceptions", "-DWITH_INSPECTOR=1"
                        cppFlags "-std=c++17"
                        targets "reacttestapp_appmodules"
                    }
                } else {
                    cmake {
                        arguments "-DANDROID_STL=c++_shared",
                                  "-DNODE_MODULES_DIR=${reactNativePath}/..",
                                  "-DPROJECT_BUILD_DIR=${buildDir}",
                                  "-DREACT_ANDROID_BUILD_DIR=${reactNativePath}/ReactAndroid/build",
                                  "-DREACT_ANDROID_DIR=${reactNativePath}/ReactAndroid"
                    }
                }
            }
            if (!project.ext.react.abiSplit) {
                ndk {
                    abiFilters(*project.ext.react.architectures)
                }
            }
        } else {
            externalNativeBuild {
                cmake {
                    arguments "-DANDROID_STL=c++_shared",
                              "-DREACT_COMMON_DIR=${reactNativePath}/ReactCommon",
                              "-DREACT_JNILIBS_DIR=${buildDir}/outputs/jniLibs"
                    cppFlags "-std=c++17"
                }
            }
        }
    }

    if (enableNewArchitecture) {
        externalNativeBuild {
            if (reactNativeVersion > 0 && reactNativeVersion < 7000) {
                ndkBuild {
                    path "${projectDir}/src/main/jni/Android.mk"
                }
            } else {
                cmake {
                    path "${projectDir}/src/main/jni/CMakeLists.txt"
                }
            }
        }

        if (!usePrefabs) {
            def reactAndroidProjectDir = project(":ReactAndroid").projectDir
            def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
                dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
                from("${reactAndroidProjectDir}/src/main/jni/prebuilt/lib")
                into("${buildDir}/react-ndk/exported")
            }
            def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
                dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
                from("${reactAndroidProjectDir}/src/main/jni/prebuilt/lib")
                into("${buildDir}/react-ndk/exported")
            }

            afterEvaluate {
                preDebugBuild.dependsOn(packageReactNdkDebugLibs)
                preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)

                if (reactNativeVersion < 7000) {
                    // Due to a bug in AGP, we have to explicitly set a dependency
                    // between configureNdkBuild* tasks and the preBuild tasks. This can
                    // be removed once this issue is resolved:
                    // https://issuetracker.google.com/issues/207403732
                    configureNdkBuildRelease.dependsOn(preReleaseBuild)
                    configureNdkBuildDebug.dependsOn(preDebugBuild)
                    project.ext.react.architectures.each { architecture ->
                        tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
                            dependsOn("preDebugBuild")
                        }
                        tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
                            dependsOn("preReleaseBuild")
                        }
                    }
                } else {
                    // Due to a bug in AGP, we have to explicitly set a dependency
                    // between configureCMakeDebug* tasks and the preBuild tasks. This can
                    // be removed once this issue is resolved:
                    // https://issuetracker.google.com/issues/207403732
                    configureCMakeDebug.dependsOn(preDebugBuild)
                    configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
                    project.ext.react.architectures.each { architecture ->
                        tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
                            dependsOn("preDebugBuild")
                        }
                        tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
                            dependsOn("preReleaseBuild")
                        }
                    }
                }
            }
        }
    } else {
        externalNativeBuild {
            cmake {
                path "${projectDir}/src/main/jni/CMakeLists.txt"
            }
        }

        if (!usePrefabs) {
            def version = getPackageVersion("react-native", rootDir)
            def allAar = file("${reactNativePath}/android/com/facebook/react/react-native/${version}/react-native-${version}.aar")

            def prepareDebugJSI = tasks.register("prepareDebugJSI", Copy) {
                def debugAar = file("${reactNativePath}/android/com/facebook/react/react-native/${version}/react-native-${version}-debug.aar")
                from(zipTree(debugAar.exists() ? debugAar : allAar).matching({ it.include "**/libjsi.so" }))
                into("${buildDir}/outputs/jniLibs/debug")
            }

            def prepareReleaseJSI = tasks.register("prepareReleaseJSI", Copy) {
                def releaseAar = file("${reactNativePath}/android/com/facebook/react/react-native/${version}/react-native-${version}-release.aar")
                from(zipTree(releaseAar.exists() ? releaseAar : allAar).matching({ it.include "**/libjsi.so" }))
                into("${buildDir}/outputs/jniLibs/release")
            }

            afterEvaluate {
                preDebugBuild.dependsOn(prepareDebugJSI)
                preReleaseBuild.dependsOn(prepareReleaseJSI)
            }
        }
    }

    lintOptions {
        lintConfig file("lint.xml")
    }

    packagingOptions {
        pickFirst "lib/armeabi-v7a/libc++_shared.so"
        pickFirst "lib/arm64-v8a/libc++_shared.so"
        pickFirst "lib/x86_64/libc++_shared.so"
        pickFirst "lib/x86/libc++_shared.so"
    }

    project.ext.signingConfigs.each { name, config ->
        def signingConfig = signingConfigs.findByName(name) ?: signingConfigs.create(name)
        signingConfig.keyAlias = config["keyAlias"]
        signingConfig.keyPassword = config["keyPassword"]
        signingConfig.storeFile = config["storeFile"]
        signingConfig.storePassword = config["storePassword"]

        def buildType = buildTypes.findByName(name) ?: buildTypes.create(name)
        buildType.signingConfig = signingConfigs[name]
    }

    sourceSets {
        main.java.srcDirs += [
            project.ext.react.enableCamera ? "src/camera/java" : "src/no-camera/java",

            // TODO: Remove this block when we drop support for 0.67
            project.ext.react.enableFabric ? "src/fabric/java" : "src/no-fabric/java",

            // TODO: Remove this block when we drop support for 0.65
            enableNewArchitecture ? "src/turbomodule/java" : "src/no-turbomodule/java",

            // TODO: Remove this block when we drop support for 0.67
            // https://github.com/facebook/react-native/commit/ce74aa4ed335d4c36ce722d47937b582045e05c4
            reactNativeVersion > 0 && reactNativeVersion < 6800
                ? "src/reactinstanceeventlistener-pre-0.68/java"
                : "src/reactinstanceeventlistener-0.68/java",

            // TODO: Remove this block when we drop support for 0.71
            // https://github.com/facebook/react-native/commit/e5dd9cdc6688e63e75a7e0bebf380be1a9a5fe2b
            reactNativeVersion > 0 && reactNativeVersion < 7200
                ? "src/reactactivitydelegate-pre-0.72/java"
                : "src/reactactivitydelegate-0.72/java",

            // TODO: Remove this block when we drop support for 0.72
            // https://github.com/facebook/react-native/commit/da358d0ec7a492edb804b9cdce70e7516ee518ae
            reactNativeVersion > 0 && reactNativeVersion < 7300
                ? "src/devserverhelper-pre-0.73/java"
                : "src/devserverhelper-0.73/java",

            // TODO: Remove this block when we drop support for 0.72
            // https://github.com/facebook/react-native/commit/c3f672cef7d4f287d3d729d33650f917ed132a0c
            reactNativeVersion > 0 && reactNativeVersion < 7300
                ? "src/reactapplication-pre-0.73/java"
                : "src/reactapplication-0.73/java",
        ]

        if (project.ext.react.enableFlipper) {
            debug.java.srcDirs += "src/flipper/java"
        }
    }

    splits {
        abi {
            reset()
            enable(project.ext.react.abiSplit)
            universalApk(false)
            include(*project.ext.react.architectures)
        }
    }
}

dependencies {
    implementation project(":support")

    if (project.ext.react.enableHermes) {
        if (autodetectReactNativeVersion) {
            implementation("com.facebook.react:hermes-android")
        } else if (reactNativeVersion >= 6900) {
            implementation("com.facebook.react:hermes-engine:+") {
                exclude(group: "com.facebook.fbjni")
            }
        } else {
            // TODO: Remove this block when we drop support for 0.68.
            def hermesEngineDir =
                findNodeModulesPath("hermes-engine", reactNativePath)
                    ?: findNodeModulesPath("hermesvm", reactNativePath)
            if (hermesEngineDir == null) {
                throw new GradleException("Could not find 'hermes-engine'. Please make sure you've added it to 'package.json'.")
            }

            def hermesAndroidDir = "${hermesEngineDir}/android"
            releaseImplementation files("${hermesAndroidDir}/hermes-release.aar")
            debugImplementation files("${hermesAndroidDir}/hermes-debug.aar")
        }
    }

    if (autodetectReactNativeVersion) {
        implementation("com.facebook.react:react-android")
    } else if (enableNewArchitecture) {
        // This is only valid for 0.68 - 0.70. From 0.71 and on, we should be
        // using prefabs.
        implementation project(":ReactAndroid")
    } else {
        def version = getPackageVersion("react-native", rootDir)
        implementation("com.facebook.react:react-native:${version}")
    }

    if (reactNativeVersion > 0 && reactNativeVersion < 6800) {
        // androidx.appcompat:appcompat:1.4.0+ breaks TextInput. This was fixed
        // in react-native 0.68. For more details, see
        // https://github.com/facebook/react-native/issues/31572.
        implementation(["androidx.appcompat", "appcompat", "1.3.1"].join(":"))
    } else {
        implementation libraries.androidAppCompat
    }
    implementation libraries.androidCoreKotlinExtensions
    implementation libraries.androidRecyclerView
    implementation libraries.androidSwipeRefreshLayout
    implementation libraries.materialComponents

    implementation libraries.moshiKotlin
    ksp libraries.moshiKotlinCodegen

    testImplementation libraries.junit
    testImplementation libraries.androidJUnitKotlinExtensions
    testImplementation libraries.mockitoInline
    androidTestImplementation libraries.androidJUnit
    androidTestImplementation libraries.androidEspressoCore

    if (project.ext.react.enableCamera) {
        implementation libraries.androidCamera
        implementation libraries.androidCameraMlKitVision
        implementation libraries.mlKitBarcodeScanning
    }

    if (project.ext.react.enableFlipper) {
        def flipperVersion = project.ext.react.enableFlipper
        debugImplementation("com.facebook.flipper:flipper:${flipperVersion}") {
            exclude(group: "com.facebook.fbjni")
        }
        debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${flipperVersion}") {
            exclude(group: "com.facebook.flipper")
        }
        debugImplementation("com.facebook.flipper:flipper-network-plugin:${flipperVersion}") {
            exclude(group: "com.facebook.flipper")
        }
    }
}

if (!usePrefabs) {
    configurations.all {
        if (enableNewArchitecture) {
            resolutionStrategy.dependencySubstitution {
                substitute(module("com.facebook.react:react-native"))
                    .using(project(":ReactAndroid"))
                    .because("On New Architecture, we are building React Native from source")
                substitute(module("com.facebook.react:hermes-engine"))
                    .using(project(":ReactAndroid:hermes-engine"))
                    .because("On New Architecture, we are building Hermes from source")
            }
        } else {
            resolutionStrategy {
                // Force version here otherwise Gradle will pick up a newer version:
                // https://github.com/facebook/react-native/issues/35210
                def version = getPackageVersion("react-native", rootDir)
                force("com.facebook.react:react-native:${version}")
                force("com.facebook.react:hermes-engine:${version}")
            }
        }
    }
}

// `@react-native-community/cli` currently requires this function to be defined.
// See https://github.com/react-native-community/cli/blob/a87fb9014635fe84ab19a1a88d6ecbbc530eb4e2/packages/platform-android/native_modules.gradle#L497
def isNewArchitectureEnabled() {
    return isFabricEnabled(project)
}
