buildscript {
    def androidDir = "${buildscript.sourceFile.getParent()}/../"
    apply(from: "${androidDir}/autolink.gradle")
    apply(from: "${androidDir}/dependencies.gradle")
    apply(from: "${androidDir}/manifest.gradle")
}

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

// `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"

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

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

    react {
        reactNativeDir = reactNativePath
        codegenDir = file(
            reactNativeVersion >= v(0, 72, 0)
                ? findNodeModulesPath("@react-native/codegen", reactNativePath)
                : findNodeModulesPath("react-native-codegen", reactNativePath)
        )
    }

    // 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
        }
    }
}

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

    mavenCentral()
    google()

    // 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(project),
    bundleInRelease      : false,
    enableCamera         : !getSingleAppMode(),
    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
        }
    }

    kotlinOptions {
        allWarningsAsErrors = true
    }

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

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

        buildConfigField "boolean", "REACTAPP_USE_FABRIC", enableNewArchitecture.toString()
        buildConfigField "boolean", "REACTAPP_USE_BRIDGELESS", enableBridgeless.toString()

        manifestPlaceholders = [
            rntaEnableCamera: project.ext.react.enableCamera ? "1000000" : "1"
        ]

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

        def cppStd = reactNativeVersion >= v(0, 74, 0) ? "-std=c++20" : "-std=c++17"
        if (enableNewArchitecture) {
            externalNativeBuild {
                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"
                    cppFlags cppStd, "-frtti", "-fexceptions", "-DWITH_INSPECTOR=1"
                }
            }
            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 cppStd, "-Wall", "-Werror", "-frtti", "-fexceptions", "-DWITH_INSPECTOR=1"
                }
            }
        }
    }

    externalNativeBuild {
        cmake {
            path "${projectDir}/src/main/jni/CMakeLists.txt"
        }
    }

    if (!enableNewArchitecture && !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]
    }
    if (buildTypes.release.signingConfig == null) {
        logger.warn("Signing config for 'release' build type not found; reusing debug config")
        buildTypes.release.signingConfig = signingConfigs.debug
    }

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

            !enableNewArchitecture
                ? "src/old-arch/java"
                : reactNativeVersion >= v(0, 81, 0)
                    ? "src/new-arch-0.81/java"
                    : reactNativeVersion >= v(0, 73, 0)
                        ? "src/new-arch-0.73/java"
                        : "src/new-arch/java",

            // TODO: Remove this block when we drop support for 0.74
            // https://github.com/facebook/react-native/commit/3283202248a36dbda553745afc46a3e3e2ab41a6
            reactNativeVersion >= v(0, 75, 0)
                ? "src/reactactivitydelegate-0.75/java"
                // TODO: Remove this block when we drop support for 0.73
                : reactNativeVersion >= v(0, 74, 0)
                    ? "src/reactactivitydelegate-0.74/java"
                    // TODO: Remove this block when we drop support for 0.71
                    // https://github.com/facebook/react-native/commit/e5dd9cdc6688e63e75a7e0bebf380be1a9a5fe2b
                    : reactNativeVersion >= v(0, 72, 0)
                        ? "src/reactactivitydelegate-0.72/java"
                        : "src/reactactivitydelegate-pre-0.72/java",

            // TODO: Remove this block when we drop support for 0.74
            // https://github.com/facebook/react-native/commit/a1e81185416a53c7c7d0cfc67e40079fd0073e7c
            reactNativeVersion >= v(0, 75, 0)
                ? "src/devserverhelper-0.75/java"
                // TODO: Remove this block when we drop support for 0.73
                // https://github.com/facebook/react-native/commit/cfa02eec50469059542ccbacbc51643b525ad461
                : reactNativeVersion >= v(0, 74, 0)
                    ? "src/devserverhelper-0.74/java"
                    // TODO: Remove this block when we drop support for 0.72
                    // https://github.com/facebook/react-native/commit/da358d0ec7a492edb804b9cdce70e7516ee518ae
                    : reactNativeVersion >= v(0, 73, 0)
                        ? "src/devserverhelper-0.73/java"
                        : "src/devserverhelper-pre-0.73/java",

            // TODO: Remove this block when we drop support for 0.75
            // https://github.com/react-native-community/template/commit/f738a366b194dd21d4d2bc14c9215b630714dd70
            reactNativeVersion >= v(0, 76, 0)
                ? "src/reactapplication-0.76/java"
                // TODO: Remove this block when we drop support for 0.72
                // https://github.com/facebook/react-native/commit/c3f672cef7d4f287d3d729d33650f917ed132a0c
                : reactNativeVersion < v(0, 73, 0)
                    ? "src/reactapplication-pre-0.73/java"
                    : "src/reactapplication-0.73/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 {
            implementation("com.facebook.react:hermes-engine:+") {
                exclude(group: "com.facebook.fbjni")
            }
        }
    }

    if (autodetectReactNativeVersion) {
        implementation("com.facebook.react:react-android")
    } else {
        def version = getPackageVersion("react-native", rootDir)
        implementation("com.facebook.react:react-native:${version}")
    }

    implementation libraries.androidAppCompat
    implementation libraries.androidCoreKotlinExtensions
    implementation libraries.androidRecyclerView
    implementation libraries.materialComponents

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

    if (reactNativeVersion == 0 || reactNativeVersion >= v(0, 75, 0)) {
        // https://github.com/facebook/react-native/blob/b0c0bb45911434ea654ba7e2feff4686061eba7a/packages/react-native-gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactExtension.kt#L162
        def dependencies = autolinkingInfo(buildDir)
        dependencies.each { path, info ->
            info.configurations.each { configuration ->
                add(configuration, project(path))
            }
        }
    }
}

if (!enableNewArchitecture && !usePrefabs) {
    configurations.all {
        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)
}
