import groovy.json.JsonSlurper
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.initialization.DefaultSettings

import java.nio.file.Paths

private static void applyConfigPlugins(String testAppDir, File rootDir) {
    String[] patch = ["node", "${testAppDir}/scripts/apply-config-plugins.mjs"]
    def stderr = new StringBuffer()
    def proc = Runtime.runtime.exec(patch, null, rootDir)
    proc.waitForProcessOutput(null, stderr)
    if (proc.exitValue() != 0) {
        throw new RuntimeException("Failed to apply config plugins:\n${stderr}")
    }
}

// TODO: Remove when `@react-native-community/cli` 6.0+ is required. See also
// https://github.com/react-native-community/cli/commit/fa0d09b2c9be144bbdff526bb14f171d7ddca88e
private static void patchArgumentTypeMismatchError(String testAppDir, File rootDir) {
    // We need to delegate this to a separate script to avoid running out of
    // Java heap space.
    String[] patch = ["node", "${testAppDir}/scripts/patch-cli-platform-android.js"]
    Runtime.runtime.exec(patch, null, rootDir).waitFor()
}

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

checkEnvironment(rootDir)
patchArgumentTypeMismatchError(testAppDir, rootDir)

if (findNodeModulesPath("@expo/config-plugins", rootDir)) {
    applyConfigPlugins(testAppDir, rootDir)
}

def cliAndroidDir = findNodeModulesPath("@react-native-community/cli-platform-android", rootDir)
apply(from: "${cliAndroidDir}/native_modules.gradle")

ext.applyTestAppSettings = { DefaultSettings settings ->
    settings.include(":app")
    settings.include(":support")

    settings.project(":app")
        .projectDir = file("${testAppDir}/android/app")
    settings.project(":support")
        .projectDir = file("${testAppDir}/android/support")

    def reactNativeGradlePlugin =
        findNodeModulesPath("@react-native/gradle-plugin", settings.rootDir)  // >= 0.72
            ?: findNodeModulesPath("react-native-gradle-plugin", settings.rootDir)  // < 0.72
    if (reactNativeGradlePlugin != null) {
        settings.includeBuild(reactNativeGradlePlugin)
    }

    // This logic is copied from `android/dependencies.gradle`. Importing it
    // currently causes build failure because `project` is not yet defined.
    def reactNativeVersion = getPackageVersionNumber("react-native", rootDir)
    def usePrefabs = reactNativeVersion == 0 || reactNativeVersion >= 7100

    if (isNewArchitectureEnabled(settings) && !usePrefabs) {
        def reactNativeDir = findNodeModulesPath("react-native", settings.rootDir)

        settings.include(":ReactAndroid")
        settings.project(":ReactAndroid")
            .projectDir = file("${reactNativeDir}/ReactAndroid")

        settings.include(":ReactAndroid:hermes-engine")
        settings.project(":ReactAndroid:hermes-engine")
            .projectDir = file("${reactNativeDir}/ReactAndroid/hermes-engine")
    }

    applyNativeModulesSettingsGradle(settings)
}

ext.applyTestAppModule = { Project project ->
    applyNativeModulesAppBuildGradle(project)

    def isRntaProject =
        project.projectDir.getParent() != null &&
        project.rootDir == file(project.projectDir.getParent())

    def manifestFile = findFile("app.json")
    if (isRntaProject && manifestFile == null) {
        logger.warn("app.json was not found; test app integration is disabled")
        return
    }

    def manifest = new JsonSlurper().parseText(manifestFile.text)
    def resources = manifest["resources"]

    def androidResources = null
    if (resources instanceof List) {
        androidResources = resources
    } else if (resources.containsKey("android")) {
        androidResources = resources["android"]
    } else {
        throw new IllegalArgumentException(
            "The `resources` property in `app.json` has to be either a list " +
            "of paths, or must contain a nested list with the `android` key"
        )
    }

    def projectRoot = manifestFile.getParent()
    def androidResourceFiles = androidResources.collect {
        file("${projectRoot}/${it}")
    }
    def androidResDir = androidResourceFiles.find {
        it.isDirectory() && it.name == "res"
    }

    if (androidResDir != null) {
        androidResourceFiles.remove(androidResDir)
    }

    def generatedAssetsDir = file("${buildDir}/generated/rncli/src/main/assets/")
    generatedAssetsDir.mkdirs()

    def generatedResDir = file("${buildDir}/generated/rncli/src/main/res/")
    generatedResDir.mkdirs()

    def generatedRawDir = file("${generatedResDir}/raw")
    generatedRawDir.mkdirs()

    preBuild.dependsOn(tasks.register("copyAssets", Copy) {
        androidResourceFiles.each {
            from(it)
        }

        into(generatedAssetsDir)
    })

    preBuild.dependsOn(tasks.register("copyResources", Copy) {
        if (androidResDir != null) {
            from(androidResDir)
            into(generatedResDir)
        }
    })

    android {
        sourceSets {
            main {
                assets.srcDirs += generatedAssetsDir
                res.srcDirs += generatedResDir
                if (manifest.containsKey("android") && manifest["android"].containsKey("icons")) {
                    res.srcDirs += "${projectRoot}/${manifest["android"]["icons"]}"
                } else {
                    res.srcDirs += "${testAppDir}/android/app/src/main/res-launcher"
                }
            }
        }
    }
}
