import groovy.json.JsonSlurper
import java.nio.file.Paths
import org.gradle.initialization.DefaultSettings

def testAppDir = buildscript.sourceFile.getParent()
apply(from: "${testAppDir}/android/utils.gradle")
apply(from: "${testAppDir}/android/node.gradle")
apply(from: "${testAppDir}/android/autolink.gradle")
apply(from: "${testAppDir}/android/config-plugins.gradle")
apply(from: "${testAppDir}/android/media-types.gradle")

checkEnvironment(rootDir, testAppDir)
applyConfigPlugins(rootDir, testAppDir)

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

def importLegacyAutolinkingModule = { File startDir ->
    def cliAndroidDir = findNodeModulesPath("@react-native-community/cli-platform-android", startDir)
    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 reactNativeVersion = getPackageVersionNumber("react-native", rootDir)

    def reactNativeGradlePlugin =
        reactNativeVersion >= v(0, 72, 0)
            ? findNodeModulesPath("@react-native/gradle-plugin", reactNativeDir)
            : findNodeModulesPath("react-native-gradle-plugin", reactNativeDir)
    if (reactNativeGradlePlugin != null) {
        settings.includeBuild(reactNativeGradlePlugin)
    }

    if (reactNativeVersion == 0 || reactNativeVersion >= v(0, 75, 0)) {
        // https://github.com/facebook/react-native/blob/b0c0bb45911434ea654ba7e2feff4686061eba7a/packages/react-native-gradle-plugin/settings-plugin/src/main/kotlin/com/facebook/react/ReactSettingsExtension.kt#L39
        def output = file("${rootDir}/app/build/generated/rnta/autolinking.json")
        def projectRoot = file(findFile("package.json").getParent())
        def dependencies = autolinkModules(projectRoot, output, testAppDir)
        dependencies.each { path, info ->
            settings.include(path)
            settings.project(path).projectDir = file(info.projectDir)
        }
    } else {
        importLegacyAutolinkingModule(reactNativeDir)
        applyNativeModulesSettingsGradle(settings)
    }

    if (settings.hasProperty("react.buildFromSource") && settings["react.buildFromSource"] == "true") {
        // https://reactnative.dev/contributing/how-to-build-from-source
        settings.includeBuild(reactNativeDir) {
            dependencySubstitution {
                substitute(module("com.facebook.react:react-android")).using(project(":packages:react-native:ReactAndroid"))
                substitute(module("com.facebook.react:react-native")).using(project(":packages:react-native:ReactAndroid"))
                substitute(module("com.facebook.react:hermes-android")).using(project(":packages:react-native:ReactAndroid:hermes-engine"))
                substitute(module("com.facebook.react:hermes-engine")).using(project(":packages:react-native:ReactAndroid:hermes-engine"))
            }
        }
    }
}

ext.applyTestAppModule = { Project project ->
    def reactNativeVersion = getPackageVersionNumber("react-native", rootDir)
    if (reactNativeVersion >= 0 && reactNativeVersion < v(0, 75, 0)) {
        importLegacyAutolinkingModule(reactNativeDir)
        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 appManifest = new JsonSlurper().parseText(manifestFile.text)
    def resources = appManifest["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 generatedMainDir = file("${buildDir}/generated/rnta/src/main")
    def generatedManifestFile = "${generatedMainDir}/AndroidManifest.xml"

    def generatedAssetsDir = file("${generatedMainDir}/assets")
    generatedAssetsDir.mkdirs()

    def generatedResDir = file("${generatedMainDir}/res")
    generatedResDir.mkdirs()

    // https://github.com/react-native-community/cli/blob/be880b86cdb3f4ea104cf232b95d11f84613321b/packages/cli-platform-android/native_modules.gradle#L534
    def generatedSrcDir = file("${generatedMainDir}/java")
    generatedSrcDir.mkdirs()

    // https://github.com/facebook/react-native/blob/3dfedbc1aec18a4255e126fde96d5dc7b1271ea7/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/assets/ReactFontManager.java#L25
    def generatedFontsDir = file("${generatedAssetsDir}/fonts")
    generatedFontsDir.mkdirs()

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

    preBuild.dependsOn(tasks.register("copyAssets", Copy) {
        androidResourceFiles.each {
            if (!isFontFile(it) && !isMediaFile(it)) {
                from(it)
            }
        }

        into(generatedAssetsDir)
    })

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

        into(generatedFontsDir)
    })

    preBuild.dependsOn(tasks.register("copyRawResources", Copy) {
        androidResourceFiles.each {
            if (isMediaFile(it)) {
                from(it) {
                    // File-based resource names must contain only lowercase
                    // a-z, 0-9, or underscore
                    rename {
                        it.toLowerCase()
                    }
                }
            }
        }

        into(generatedRawDir)
    })

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

    preBuild.dependsOn(tasks.register("embedAppManifest", Exec) {
        workingDir(projectRoot)
        commandLine("node", "${testAppDir}/scripts/embed-manifest/kotlin.mjs")
        standardOutput = new ByteArrayOutputStream()

        doLast {
            def manifestProvider = file("${generatedSrcDir}/com/microsoft/reacttestapp/manifest/ManifestProvider.kt")
            file(manifestProvider.parent).mkdirs()
            manifestProvider.withWriter { w ->
                w << standardOutput.toString()
            }
        }
    })

    preBuild.dependsOn(tasks.register("generateAndroidManifest", Exec) {
        workingDir(projectRoot)
        commandLine(
            "node",
            "${testAppDir}/android/android-manifest.js",
            manifestFile,
            generatedManifestFile)
    })

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