import cz.habarta.typescript.generator.gradle.GenerateTask
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import org.apache.tools.ant.filters.ReplaceTokens

import java.time.Instant

plugins {
    id 'java'
    id 'base'
    id "com.gradleup.shadow" version "8.3.6"
    id "com.palantir.git-version" version "3.1.0"
    id "com.github.hierynomus.license-report" version "0.16.1"
    id "org.jetbrains.gradle.plugin.idea-ext" version "1.1.10"
    id 'cz.habarta.typescript-generator' version "3.2.+"
}

apply plugin: com.hierynomus.gradle.license.LicenseReportingPlugin

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
        vendor = JvmVendorSpec.ADOPTIUM
    }
}

group = "dev.ftb"

base {
    archivesName = "app"
}

def packageJson = new JsonSlurper().parse(file("../package.json"))
def appVersion = packageJson.version

assert appVersion != null && appVersion != "", "package.json must have a version field"

def branch = "release"
if (appVersion.contains("beta")) {
    branch = "beta"
} else if (appVersion.contains("alpha")) {
    branch = "alpha"
}

def details = versionDetails()
version = appVersion

repositories {
    mavenLocal()
    mavenCentral()
    
    maven { 
        url = 'https://maven.creeperhost.net'
        content {
            includeGroup("net.covers1624")
        }
    }
    
    maven { 
        url = "https://repo.gradle.org/artifactory/libs-releases/"
        content {
            includeGroupByRegex("net.rubygrapefruit.*")
        }
    }
}

dependencies {
    def log4jVersion = '2.25.1'
    
    implementation "org.apache.logging.log4j:log4j-core:$log4jVersion"
    implementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion"
    implementation "org.apache.logging.log4j:log4j-jul:$log4jVersion"

    implementation 'net.sf.jopt-simple:jopt-simple:5.0.4'

    implementation 'com.google.code.gson:gson:2.10.1'
    implementation 'org.nanohttpd:nanohttpd:2.3.1'
    implementation 'org.nanohttpd:nanohttpd-websocket:2.3.1'
    implementation 'com.github.oshi:oshi-core:5.7.0'
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'org.apache.commons:commons-compress:1.27.1'
    implementation 'commons-io:commons-io:2.19.0'
    implementation('org.apache.maven:maven-artifact:3.8.4') {
        exclude group: 'org.codehaus.plexus' // Not needed for this application.
    }
    implementation 'net.covers1624:Quack:0.4.10.108'
    implementation 'net.covers1624:JdkUtils:0.6.0.21'
    implementation 'com.google.guava:guava:33.4.8-jre'
    implementation 'org.semver4j:semver4j:5.6.0'
    implementation 'com.electronwill.night-config:toml:3.6.4'
    
    compileOnly 'org.jetbrains:annotations:22.0.0'
    compileOnly 'com.google.code.findbugs:jsr305:3.0.2'

//    if (file('debugger').exists()) {
//        runtimeOnly project(":debugger")
//    }

    testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    testImplementation 'org.nanohttpd:nanohttpd:2.3.1'
}

test {
    useJUnitPlatform()
}

tasks.register('processSource', Sync) {
    from sourceSets.main.java
    inputs.property 'version', version
    filter(ReplaceTokens, tokens: [APPVERSION: version, BRANCH: branch, COMMIT: details.gitHash])
    into "$buildDir/src"
}

compileJava {
    options.encoding = "UTF-8"
    dependsOn processSource
    source = processSource.outputs
}

jar.enabled = false
shadowJar {
    transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer)
    manifest {
        attributes([
                "Specification-Title"   : "App",
                "Specification-Vendor"  : "Feed The Beast LTD",
                "Specification-Version" : version,
                "Implementation-Title"  : "AppMain",
                "Implementation-Version": version,
                "Implementation-Vendor" : "Feed The Beast LTD"
        ] as LinkedHashMap, "dev/ftb/app/")
        attributes 'Multi-Release': 'true'
        attributes 'Main-Class': 'dev.ftb.app.AppMain'
    }
}
assemble.dependsOn 'shadowJar'

// TODO make root dir a bare gradle project and subprocess the submodule
project(":web") {
}

downloadLicenses {
    // Can only be the debugger, just ignore
    includeProjectDependencies = false

    // Only include things that will be part of the final jar.
    dependencyConfiguration = "runtimeClasspath"
}

def dependencySources = layout.projectDirectory.file('dependency_sources.json')
def licensesOutput = layout.buildDirectory.file('libs/java-licenses.json')
def writeLicenses = tasks.register('writeLicenses') {
    it.dependsOn 'downloadLicenses'
    inputs.file(dependencySources)
    outputs.file(licensesOutput)

    // Parse the dependency-license.json and add the repository to the object where possible.
    doLast {
        def sources = new JsonSlurper().parse(dependencySources.asFile) as Map<String, String>
        def data = new JsonSlurper().parse(new File("$buildDir/reports/license/dependency-license.json"))
        def missing = []
        def used = []
        for (obj in data['dependencies']) {
            def name = obj['name']
            if (!name.contains(':')) continue
            name = name.take(name.lastIndexOf(":"))
            def repo = sources[name]
            used << name
            if (repo == null) {
                missing << name
            } else {
                obj.repository = repo
            }
        }
        if (!missing.isEmpty()) {
            getLogger().warn("The following dependencies are missing repo links: ")
            missing.each{ getLogger().warn("  $it")}
        }

        def unused = sources.keySet() - used
        if (!unused.isEmpty()) {
            getLogger().warn("The following dependency repo links aren't required: ")
            unused.each{ getLogger().warn("  $it")}
        }


        // Write the new json.
        licensesOutput.get().asFile.text = JsonOutput.toJson(data)
    }
}

/**
 * The template & app support advanced arguments for the runtime using the following format:
 * {
 *    "key": "" // Optional, if not set, the value will be used as the key and value.
 *    "value": "",
 *    // At least one of the following must be set.
 *    "filter": {
 *       "os": "windows", // optional
 *       "arch": "x64", // optional
 *    }
 * }
 */
def metaOutput = layout.buildDirectory.file('libs/meta.json')
def writeVersion = tasks.register('writeVersion') {
    outputs.file(metaOutput)
    doLast {
        // Load the meta template file and replace specific parts of the data
        def fileData = file("./meta-template.json").text
        def template = new JsonSlurper().parseText(fileData)

        template.appVersion = version
        template.commit = details.gitHash
        template.branch = branch
        template.released = Instant.now().getEpochSecond()
        template.runtime.version = project.target_java_version
        template.runtime.jar = shadowJar.outputs.files.singleFile.name

        metaOutput.get().asFile.text = JsonOutput.toJson(template)
    }
}

tasks.named('build').configure {
    it.dependsOn writeLicenses
    it.dependsOn writeVersion
}

wrapper {
    gradleVersion = "8.7"
}

idea.project.settings {
    delegateActions {
        // Gradle runners are garbage and break frequently.
        delegateBuildRunToGradle = false
        testRunner = org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.PLATFORM
    }
    runConfigurations {
        "Start Backend"(org.jetbrains.gradle.ext.Application) {
            mainClass = "dev.ftb.app.AppMain"
            moduleName = "ftb-app.main"
            jvmArgs = '-ea '                                             // Enable assertions
            jvmArgs += '-Dconsole.level=ALL '                            // Direct ALL log levels to console.
            jvmArgs += '-DDownloadTask.debug=true '                      // Enable Quack DownloadAction logging from DownloadTask
            jvmArgs += '-DInstanceLauncher.ProgressTracker.debug=true '  // Enable debug logging for InstanceLauncher's ProgressTracker TODO Log4j markers?
            jvmArgs += '-DInstanceInstaller.debug=true '                 // Enable debug logging for InstanceInstaller.
            jvmArgs += '-DInstanceLauncher.minecraft=ACCEPT '            // Allow Minecraft logging Marker in Log4j.
            jvmArgs += '-DInstanceShareUploader.debug=true '             // Enable debug logging for InstanceShareUploader
            jvmArgs += '-DCloudSaveManager.debug=true '                  // Enable debug logging for CloudSaveManager
            programParameters = '--dev'
            // Set the working directory to the root of the project.
            workingDirectory = project.file('../.ftba')
        }
    }
}

def standardTypescriptSettings = (GenerateTask task, File file) -> {
    task.jsonLibrary = "gson"
    task.noFileComment = true
    task.noTslintDisable = true
    task.noEslintDisable = true
    task.tsNoCheck = true
    task.outputKind = "module"
    task.outputFile = file
    task.excludeClasses = [
        "java.lang.Iterable",
        "java.lang.Comparable",
        "java.lang.Cloneable",
        "java.nio.Watchable",
        "java.io.Serializable",
        "com.google.common.hash.HashCode",
        "com.google.gson.JsonElement",
        "com.google.gson.JsonArray",
        "com.google.gson.JsonObject",
        "java.awt.geom.Dimension2D"
    ]
    task.customTypeMappings = [
        "java.nio.file.Path:string"
    ]
}

tasks.named("generateTypeScript", GenerateTask) {
    standardTypescriptSettings(it, project.file("../src/core/types/javaApi.d.ts"))
    classPatterns = [
            'dev.ftb.app.api.handlers.instances.InstalledInstancesHandler$SugaredInstanceJson',
            'dev.ftb.app.api.data.*',
            'dev.ftb.app.api.data.**.*',
            'dev.ftb.app.api.data.**.*$*',
            'dev.ftb.app.api.handlers.**.*$Request',
            'dev.ftb.app.api.handlers.**.*$Reply',
            'dev.ftb.app.api.handlers.**.*$Data',
            'dev.ftb.app.storage.settings.SettingsData'
    ]
    customTypeNamingFunction = """
(name, simpleName) => {
    const prefixTargets = ["Reply", "Progress", "Request", "Data"]
    if (prefixTargets.includes(simpleName)) {
        const targetEnding = prefixTargets[prefixTargets.indexOf(simpleName)];
        const reply = name.split(".").slice(-1)[0];
        const replyClassContainer = reply.split("\$")[0];
        return replyClassContainer + targetEnding;
    } 

    return simpleName;
}
"""
}
