import groovy.json.JsonSlurper
import java.util.concurrent.TimeUnit

apply from: "ExpensiLog.gradle"

class PatchedArtifactsConfig {
    String version = null
    String packageName = null
    String githubUsername = null
    String githubToken = null
    Boolean buildFromSource = true
}

def patchedArtifacts = settings.getExtensions().create('patchedArtifacts', PatchedArtifactsConfig)

def warnIfNotConfigured(reason) {
    ExpensiLog.warn("$reason")
    ExpensiLog.warn("For setup instructions, refer to: https://github.com/Expensify/App?tab=readme-ov-file#enabling-prebuilt-react-native-artifacts-on-android")
}

def getNewDotRootDir() {
    return "${rootDir}/${hasProperty('newDotRoot') ? getProperty('newDotRoot') : "/.."}"
}

def runCommand(command) {
    def process = command.execute()
    process.waitFor(10, TimeUnit.SECONDS)
    return [output: process.in.text?.trim(), exitCode: process.exitValue()]
}

def hasGithubCLI() {
    return runCommand(["which", "gh"]).exitCode == 0
}

def hasRequiredScopes() {
    def scopes = runCommand(["gh", "auth", "status"]).output
    return ['read:packages', 'write:packages'].any { scope -> scopes.contains(scope) }
}

def getGithubUsername() {
    return new JsonSlurper().parseText(runCommand(["gh", "api", "user"]).output).login
}

def getGithubToken() {
    return runCommand(["gh", "auth", "token"]).output
}

def getCredentials() {
    if (!hasGithubCLI()) {
        warnIfNotConfigured("No Github CLI found.")
        return null
    }

    try {
        if(!hasRequiredScopes()) {
            warnIfNotConfigured("Github token does not have required scope read:packages.")
            return null
        }

        return [
            githubUsername: getGithubUsername(),
            githubToken: getGithubToken(),
        ]
    } catch (Exception e) {
        warnIfNotConfigured("Failed to get Github credentials. This might be due to an expired token or not being logged in.")
        return null
    }
}

def getReactNativeVersion() {
    def packageJsonPath = "${getNewDotRootDir()}/package.json"
    def packageJson = file(packageJsonPath)
    return new JsonSlurper().parse(packageJson).dependencies.'react-native'
}

def getArtifactsCandidates(packageName) {
    def reactNativeVersion = getReactNativeVersion()
    def patchedVersions = runCommand(["gh", "api", "/orgs/Expensify/packages/maven/com.expensify.${packageName}.react-android/versions", "--jq", ".[].name"]).output
    return patchedVersions.split("\n").findAll { it.startsWith(reactNativeVersion) }
}

def getPomFile(version, packageName, githubToken) {
    return new XmlSlurper().parse(
        new URL("https://maven.pkg.github.com/Expensify/App/com/expensify/${packageName}/react-android/${version}/react-android-${version}.pom").openConnection().with {
            setRequestProperty("Authorization", "Bearer ${githubToken}")
            getInputStream()
        }
    )
}

def getLocalPatchesHash() {
    def newDotRootDir = getNewDotRootDir()
    def computePatchesScript = "${getNewDotRootDir()}/scripts/compute-patches-hash.sh"
    def arguments = hasProperty('newDotRoot') ? ["${newDotRootDir}/patches", "${newDotRootDir}/Mobile-Expensify/patches"] : ["${newDotRootDir}/patches"]
    def localPatchesHash = runCommand([computePatchesScript] + arguments).output.trim()

    return localPatchesHash
}

def findMatchingArtifactsVersion(packageName, githubToken) {
    try {
        def localPatchesHash = getLocalPatchesHash()
        def candidates = getArtifactsCandidates(packageName)
        for (candidate in candidates) {
            def patchesHashFromPomFile = getPomFile(candidate, packageName, githubToken).properties.patchesHash.text().trim()
            if (patchesHashFromPomFile == localPatchesHash) {
                return candidate
            }
        }
    } catch (Exception e) {
        ExpensiLog.error("Failed to find matching artifacts version for ${packageName}. Reason: $e")
        return null
    }
}

settings.extensions.configure(PatchedArtifactsConfig) { config ->
    ExpensiLog.setPrefix("PatchedArtifacts")
    config.buildFromSource = getProperty('patchedArtifacts.forceBuildFromSource') == 'true'
    if(config.buildFromSource) {
        ExpensiLog.lifecycle("Forcing build from source.")
        return
    }

    def credentials = getCredentials()
    config.packageName = getProperty('patchedArtifacts.packageName')

    if(credentials != null) {
        config.githubUsername = credentials.githubUsername
        config.githubToken = credentials.githubToken
        config.version = findMatchingArtifactsVersion(config.packageName, config.githubToken)
    }

    if(config.version == null || config.packageName == null || config.githubUsername == null || config.githubToken == null) {
        config.buildFromSource = true
        ExpensiLog.lifecycle("No matching artifacts version found for ${config.packageName}. Building react-native from source.")
    }
    else {
        config.buildFromSource = false
        ExpensiLog.lifecycle("Using patched react-native artifacts: ${config.packageName}:${config.version}")
    }
}
