import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.internal.jvm.Jvm
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
import org.jetbrains.kotlin.org.apache.commons.io.FileUtils
import org.jetbrains.kotlin.org.apache.commons.io.filefilter.FileFilterUtils
import org.jetbrains.kotlin.org.apache.commons.lang3.StringUtils
import org.jetbrains.kotlin.org.apache.commons.lang3.time.DateFormatUtils
import java.io.FileNotFoundException
import java.nio.file.Files
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.Future

plugins {
    java
    idea
    application
    `maven-publish`
    alias(libs.plugins.kotlin.jvm)
    alias(libs.plugins.kotlinx.serialization)
}


group = "app.termora"
version = rootProject.projectDir.resolve("VERSION").readText().trim()

val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem()
val arch: ArchitectureInternal = DefaultNativePlatform.getCurrentArchitecture()
val appVersion = project.version.toString().split("-")[0]
val makeAppx = if (os.isWindows) StringUtils.defaultString(System.getenv("MAKEAPPX_PATH")) else StringUtils.EMPTY
val isDeb = os.isLinux && System.getenv("TERMORA_TYPE") == "deb"
val isAppx = os.isWindows && makeAppx.isNotBlank() && System.getenv("TERMORA_TYPE") == "appx"
val isBeta = project.version.toString().contains("beta", ignoreCase = true)

// macOS 签名信息
val macOSSignUsername = System.getenv("TERMORA_MAC_SIGN_USER_NAME") ?: StringUtils.EMPTY
val macOSSign = os.isMacOsX && macOSSignUsername.isNotBlank()
        && System.getenv("TERMORA_MAC_SIGN").toBoolean()

// macOS 公证信息
val macOSNotaryKeychainProfile = System.getenv("TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE") ?: StringUtils.EMPTY
val macOSNotary = macOSSign && macOSNotaryKeychainProfile.isNotBlank()
        && System.getenv("TERMORA_MAC_NOTARY").toBoolean()

fun exec(action: ExecSpec.() -> Unit) {
    providers.exec(action).result.get().assertNormalExitValue()
}

allprojects {
    repositories {
        mavenCentral()
        maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
        maven("https://www.jitpack.io")
        maven("https://central.sonatype.com/repository/maven-snapshots")
    }
}

dependencies {

    testImplementation(kotlin("test"))
    testImplementation(libs.hutool)
    testImplementation(libs.sshj)
    testImplementation(libs.jsch)
    testImplementation(libs.rhino)
    testImplementation(libs.delight.rhino.sandbox)
    testImplementation(platform(libs.testcontainers.bom))
    testImplementation(libs.testcontainers)
    testImplementation(libs.h2)
    testImplementation(libs.exposed.migration)

    api(kotlin("reflect"))
    api(libs.slf4j.api)
    api(libs.pty4j)
    api(libs.slf4j.tinylog)
    api(libs.tinylog.impl)
    api(libs.commons.codec)
    api(libs.commons.io)
    api(libs.commons.lang3)
    api(libs.commons.csv)
    api(libs.commons.net)
    api(libs.commons.text)
    api(libs.kotlinx.coroutines.swing)
    api(libs.kotlinx.coroutines.core)

    api(libs.flatlaf)
    api(libs.flatlafextras)
    api(libs.flatlafswingx)

    api(libs.kotlinx.serialization.json)
    api(libs.swingx)
    api(libs.jgoodies.forms)
    api(libs.jna)
    api(libs.jna.platform)
    api(libs.versioncompare)
    api(libs.oshi.core)
    api(libs.jSystemThemeDetector) { exclude(group = "*", module = "*") }
    api(libs.jfa) { exclude(group = "*", module = "*") }
    api(libs.jbr.api)
    api(libs.okhttp)
    api(libs.okhttp.logging)
    api(libs.sshd.core)
    api(libs.commonmark)
    api(libs.jgit)
    api(libs.jgit.sshd) { exclude(group = "*", module = "sshd-osgi") }
    api(libs.jgit.agent) { exclude(group = "*", module = "sshd-osgi") }
    api(libs.eddsa)
    api(libs.jnafilechooser)

    api(libs.colorpicker)
    api(libs.mixpanel)
    api(libs.ini4j)
    api(libs.restart4j)
    api(libs.exposed.core)
    api(libs.exposed.crypt)
    api(libs.exposed.jdbc)
    api(libs.sqlite)
    api(libs.jug)
    api(libs.semver4j)
    api(libs.jsvg)
    api(libs.dom4j) { exclude(group = "*", module = "*") }
}

application {
    val args = mutableListOf(
        "-Xmx2048m",
        "-Drelease-date=${DateFormatUtils.format(Date(), "yyyy-MM-dd")}",
        "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
    )

    if (os.isMacOsX) {
        // macOS NSWindow
        args.add("--add-opens java.desktop/java.awt=ALL-UNNAMED")
        args.add("--add-opens java.desktop/sun.lwawt=ALL-UNNAMED")
        args.add("--add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
        args.add("--add-exports java.desktop/com.apple.eawt=ALL-UNNAMED")
        args.add("-Dsun.java2d.metal=true")
        args.add("-Dapple.awt.application.appearance=system")
    }

    if (os.isLinux) {
        // https://blog.jetbrains.com/platform/2026/02/wayland-by-default-in-2026-1-eap/
        args.add("-Dawt.toolkit.name=XToolkit")
    }

    args.add("-DTERMORA_PLUGIN_DIRECTORY=${layout.buildDirectory.get().asFile.absolutePath}${File.separator}plugins")

    applicationDefaultJvmArgs = args
    mainClass = "app.termora.MainKt"
}

publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            from(components["java"])

            pom {
                name = project.name
                description = "Termora is a terminal emulator and SSH client for Windows, macOS and Linux"
                url = "https://github.com/TermoraDev/termora"

                licenses {
                    license {
                        name = "AGPL-3.0"
                        url = "https://opensource.org/license/agpl-v3"
                    }
                }

                developers {
                    developer {
                        name = "hstyi"
                        url = "https://github.com/hstyi"
                    }
                }

                scm {
                    url = "https://github.com/TermoraDev/termora"
                }
            }
        }
    }
}

tasks.processResources {
    val betaVersion = project.version.toString().substringAfterLast('.')
    filesMatching("**/AppxManifest.xml") {
        filter<ReplaceTokens>(
            "tokens" to mapOf(
                "version" to appVersion,
                "betaVersion" to if (isBeta) betaVersion else "0",
                "architecture" to if (arch.isArm64) "arm64" else "x64",
                "projectDir" to project.projectDir.absolutePath,
            )
        )
    }
}


tasks.test {
    useJUnitPlatform()
}

@Suppress("CascadeIf")
tasks.register<Copy>("copy-dependencies") {
    val dir = layout.buildDirectory.dir("libs")
    from(configurations.runtimeClasspath).into(dir)
    val jna = libs.jna.asProvider().get()
    val pty4j = libs.pty4j.get()
    val flatlaf = libs.flatlaf.get()
    val restart4j = libs.restart4j.get()
    val sqlite = libs.sqlite.get()
    val archName = if (arch.isArm) "aarch64" else "x86_64"
    val dylib = dir.get().dir("dylib").asFile

    doLast {
        for (file in dir.get().asFile.listFiles() ?: emptyArray()) {
            if ("${jna.name}-${jna.version}" == file.nameWithoutExtension) {
                val targetDir = File(dylib, jna.name)
                FileUtils.forceMkdir(targetDir)
                if (os.isWindows) {
                    // @formatter:off
                    exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/win32-${arch.name}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isLinux) {
                    // @formatter:off
                    exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/linux-${arch.name}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isMacOsX) {
                    // @formatter:off
                    exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/darwin-${arch.name}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                }

                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-*") }
                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/linux-*") }
                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/darwin-*") }
                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/sunos-*") }
                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/openbsd-*") }
                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/freebsd-*") }
                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/dragonflybsd-*") }
                exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/aix-*") }
            } else if ("${pty4j.name}-${pty4j.version}" == file.nameWithoutExtension) {
                val osName = if (os.isWindows) "win32" else if (os.isMacOsX) "darwin" else "linux"
                val myArchName = if (arch.isArm) "aarch64" else "x86-64"
                val targetDir = if (os.isMacOsX) FileUtils.getFile(dylib, pty4j.name, osName)
                else FileUtils.getFile(dylib, pty4j.name, osName, myArchName)
                FileUtils.forceMkdir(targetDir)
                if (os.isWindows) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/*win/${myArchName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isLinux) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/*linux/${myArchName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isMacOsX) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/com/pty4j/native/darwin*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                }
                exec { commandLine("zip", "-d", file.absolutePath, "resources/*") }
            } else if ("${restart4j.name}-${restart4j.version}" == file.nameWithoutExtension) {
                val targetDir = FileUtils.getFile(dylib, restart4j.name)
                FileUtils.forceMkdir(targetDir)
                if (os.isWindows) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "win32/${archName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isLinux) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "linux/${archName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isMacOsX) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "darwin/${archName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                }
                // 设置可执行权限
                for (e in FileUtils.listFiles(
                    targetDir,
                    FileFilterUtils.trueFileFilter(),
                    FileFilterUtils.falseFileFilter()
                )) e.setExecutable(true)
                exec { commandLine("zip", "-d", file.absolutePath, "win32/*") }
                exec { commandLine("zip", "-d", file.absolutePath, "darwin/*") }
                exec { commandLine("zip", "-d", file.absolutePath, "linux/*") }
            } else if ("${sqlite.name}-${sqlite.version}" == file.nameWithoutExtension) {
                val targetDir = FileUtils.getFile(dylib, sqlite.name)
                FileUtils.forceMkdir(targetDir)
                if (os.isWindows) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Windows/${archName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isLinux) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Linux/${archName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isMacOsX) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Mac/${archName}/*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                }
                exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/*") }
            } else if ("${flatlaf.name}-${flatlaf.version}" == file.nameWithoutExtension) {
                val targetDir = FileUtils.getFile(dylib, flatlaf.name)
                FileUtils.forceMkdir(targetDir)
                val isArm = arch.isArm
                if (os.isWindows) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*windows*${if (isArm) "arm64" else "x86_64"}*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isLinux) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*linux*${if (isArm) "arm64" else "x86_64"}*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                } else if (os.isMacOsX) {
                    // @formatter:off
                    exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*macos*${if (isArm) "arm" else "x86"}*", "-d", targetDir.absolutePath) }
                    // @formatter:on
                }
                exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*") }
            }
        }

        // 对二进制签名
        if (os.isMacOsX) {
            Files.walk(dylib.toPath()).use { paths ->
                for (path in paths) {
                    if (Files.isRegularFile(path)) {
                        signMacOSLocalFile(path.toFile())
                    }
                }
            }
        }
    }

}

tasks.register<Exec>("jlink") {
    val modules = listOf(
        "java.base",
        "java.desktop",
        "java.logging",
        "java.management",
        "java.rmi",
        "java.sql",
        "java.security.jgss",
        "jdk.crypto.ec",
        "jdk.unsupported",
        "jdk.httpserver",
    )

    commandLine(
        "${Jvm.current().javaHome}/bin/jlink",
        "--verbose",
        "--strip-java-debug-attributes",
        "--strip-native-commands",
        "--strip-debug",
        "--compress=zip-9",
        "--no-header-files",
        "--no-man-pages",
        "--add-modules",
        modules.joinToString(","),
        "--output",
        "${layout.buildDirectory.get()}/jlink"
    )
}

tasks.register<Exec>("jpackage") {

    val buildDir = layout.buildDirectory.get()
    val options = mutableListOf(
        "-Xmx2048m",
        "-XX:+HeapDumpOnOutOfMemoryError",
        "-Dlogger.console.level=off",
        "-Dkotlinx.coroutines.debug=off",
        "-Dapp-version=${project.version}",
        "-Drelease-date=${DateFormatUtils.format(Date(), "yyyy-MM-dd")}",
        "--add-exports java.base/sun.nio.ch=ALL-UNNAMED",
    )

    options.add("-Dsun.java2d.metal=true")

    if (os.isMacOsX) {
        // NSWindow
        options.add("-Dapple.awt.application.appearance=system")
        options.add("--add-opens java.desktop/java.awt=ALL-UNNAMED")
        options.add("--add-opens java.desktop/sun.font=ALL-UNNAMED")
        options.add("--add-opens java.desktop/sun.lwawt=ALL-UNNAMED")
        options.add("--add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
        options.add("--add-opens java.desktop/sun.lwawt.macosx.concurrent=ALL-UNNAMED")
        options.add("--add-exports java.desktop/com.apple.eawt=ALL-UNNAMED")
    }

    if (os.isLinux) {
        options.add("--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED")
        if (isDeb) {
            options.add("-Djpackage.app-layout=deb")
        }
    }

    val arguments = mutableListOf("${Jvm.current().javaHome}/bin/jpackage")
    arguments.addAll(listOf("--runtime-image", "${buildDir}/jlink"))
    arguments.addAll(listOf("--name", project.name.uppercaseFirstChar()))
    arguments.addAll(listOf("--app-version", appVersion))
    arguments.addAll(listOf("--main-jar", tasks.jar.get().archiveFileName.get()))
    arguments.addAll(listOf("--main-class", application.mainClass.get()))
    arguments.addAll(listOf("--input", "$buildDir/libs"))
    arguments.addAll(listOf("--temp", "$buildDir/jpackage"))
    arguments.addAll(listOf("--dest", "$buildDir/distributions"))
    arguments.addAll(listOf("--java-options", options.joinToString(StringUtils.SPACE)))
    arguments.addAll(listOf("--vendor", "TermoraDev"))
    arguments.addAll(listOf("--copyright", "TermoraDev"))
    arguments.addAll(listOf("--app-content", "$buildDir/plugins"))

    if (os.isMacOsX) {
        arguments.addAll(listOf("--mac-package-name", project.name.uppercaseFirstChar()))
        arguments.addAll(listOf("--mac-app-category", "developer-tools"))
        arguments.addAll(listOf("--mac-package-identifier", "${project.group}"))
        arguments.addAll(listOf("--icon", "${projectDir.absolutePath}/src/main/resources/icons/termora.icns"))
    }

    if (os.isWindows) {
        arguments.addAll(listOf("--icon", "${projectDir.absolutePath}/src/main/resources/icons/termora.ico"))
    }

    if (os.isLinux) {
        arguments.addAll(listOf("--icon", "${projectDir.absolutePath}/src/main/resources/icons/termora.png"))
    }


    arguments.add("--type")
    if (os.isMacOsX) {
        arguments.add("dmg")
    } else if (os.isWindows) {
        arguments.add("app-image")
    } else if (os.isLinux) {
        arguments.add(if (isDeb) "deb" else "app-image")
        if (isDeb) {
            arguments.add("--linux-deb-maintainer")
            arguments.add("support@termora.app")
        }
    } else {
        throw UnsupportedOperationException()
    }

    if (macOSSign) {
        arguments.add("--mac-sign")
        arguments.add("--mac-signing-key-user-name")
        arguments.add(macOSSignUsername)
    }

    commandLine(arguments)

}

tasks.register("dist") {
    doLast {
        val osName = if (os.isMacOsX) "osx" else if (os.isWindows) "windows" else "linux"
        val distributionDir = layout.buildDirectory.dir("distributions").get()
        val finalFilenameWithoutExtension = "${project.name}-${project.version}-${osName}-${arch.name}"
        val projectName = project.name.uppercaseFirstChar()

        if (os.isWindows) {
            packOnWindows(distributionDir, finalFilenameWithoutExtension, projectName)
        } else if (os.isLinux) {
            packOnLinux(distributionDir, finalFilenameWithoutExtension, projectName)
        } else if (os.isMacOsX) {
            packOnMac(distributionDir, finalFilenameWithoutExtension, projectName)
        } else {
            throw GradleException("${os.name} is not supported")
        }
    }
}

tasks.register("check-license") {
    doLast {
        val iterator = File(projectDir, "THIRDPARTY").readLines().iterator()
        val thirdPartyNames = mutableSetOf<String>()

        while (iterator.hasNext()) {
            val name = iterator.next()
            if (name.isBlank()) {
                continue
            }

            // ignore license name
            iterator.next()
            // ignore license url
            iterator.next()

            thirdPartyNames.add(name)
        }

        for (dependency in configurations.runtimeClasspath.get().allDependencies) {
            if (!thirdPartyNames.contains(dependency.name)) {
                throw GradleException("${dependency.name} No license found")
            }
        }
    }
}


/**
 * 创建 zip、msi
 */
fun packOnWindows(distributionDir: Directory, finalFilenameWithoutExtension: String, projectName: String) {
    val dir = layout.buildDirectory.dir("distributions").get().asFile
    val cfg = FileUtils.getFile(dir, projectName, "app", "${projectName}.cfg")
    val configText = cfg.readText()

    // appx
    if (isAppx) {
        cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=appx").toString())
        val appxManifest = FileUtils.getFile(dir, projectName, "AppxManifest.xml")
        layout.buildDirectory.file("resources/main/AppxManifest.xml").get().asFile
            .renameTo(appxManifest)
        val icons = setOf("termora.png", "termora_44x44.png", "termora_150x150.png")
        for (file in projectDir.resolve("src/main/resources/icons/").listFiles()) {
            if (icons.contains(file.name)) {
                val p = appxManifest.parentFile.resolve("icons/${file.name}")
                FileUtils.forceMkdirParent(p)
                file.copyTo(p, true)
            }
        }
        exec {
            commandLine(makeAppx, "pack", "/d", projectName, "/p", "${finalFilenameWithoutExtension}.msix")
            workingDir = dir
        }
        return
    }

    // zip
    cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=zip").toString())
    exec {
        commandLine(
            "tar", "-vacf",
            distributionDir.file("${finalFilenameWithoutExtension}.zip").asFile.absolutePath,
            projectName
        )
        workingDir = dir
    }

    // exe
    cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=exe").toString())
    exec {
        commandLine(
            "iscc",
            "/DMyAppId=${projectName}",
            "/DMyAppName=${projectName}",
            "/DMyAppVersion=${appVersion}",
            "/DMyOutputDir=${distributionDir.asFile.absolutePath}",
            "/DMySetupIconFile=${FileUtils.getFile(projectDir, "src", "main", "resources", "icons", "termora.ico")}",
            "/DMyWizardSmallImageFile=${
                FileUtils.getFile(
                    projectDir,
                    "src",
                    "main",
                    "resources",
                    "icons",
                    "termora_128x128.bmp"
                )
            }",
            "/DMySourceDir=${FileUtils.getFile(dir, projectName).absolutePath}",
            "/F${finalFilenameWithoutExtension}",
            FileUtils.getFile(projectDir, "src", "main", "resources", "termora.iss")
        )
    }

}

/**
 * 对于 macOS 先对 jpackage 构建的 dmg 重命名 -> 签名 -> 公证，另外还会创建一个 zip 包
 */
fun packOnMac(distributionDir: Directory, finalFilenameWithoutExtension: String, projectName: String) {
    val dmgFile = distributionDir.file("${finalFilenameWithoutExtension}.dmg").asFile
    val zipFile = distributionDir.file("${finalFilenameWithoutExtension}.zip").asFile

    // rename
    // @formatter:off
    exec { commandLine("mv", distributionDir.file("${projectName}-${appVersion}.dmg").asFile.absolutePath, dmgFile.absolutePath,) }
    // @formatter:on

    // sign dmg
    signMacOSLocalFile(dmgFile)

    // 找到 .app
    val imageFile = layout.buildDirectory.dir("jpackage/image/").get().asFile
    val appFile = imageFile.listFiles()?.firstOrNull() ?: throw FileNotFoundException("${projectName}.app")

    // zip
    // @formatter:off
    exec { commandLine("ditto", "-c", "-k", "--sequesterRsrc", "--keepParent", appFile.absolutePath, zipFile.absolutePath) }
    // @formatter:on

    // sign zip
    signMacOSLocalFile(zipFile)

    // 公证
    if (macOSNotary) {
        val pool = Executors.newCachedThreadPool()
        val jobs = mutableListOf<Future<*>>()

        // zip
        pool.submit {
            // 对 zip 公证
            notaryMacOSLocalFile(zipFile)
            // 对 .app 盖章
            stapleMacOSLocalFile(appFile)
            // 删除旧的 zip ，旧的 zip 仅仅是为了公证
            FileUtils.deleteQuietly(zipFile)
            // 再对盖完章的 app 打成 zip 包
            // @formatter:off
            exec { commandLine("ditto", "-c", "-k", "--sequesterRsrc", "--keepParent", appFile.absolutePath, zipFile.absolutePath) }
            // @formatter:on
            // 再对 zip 签名
            signMacOSLocalFile(zipFile)
        }.apply { jobs.add(this) }

        // dmg
        pool.submit {
            // 公证
            notaryMacOSLocalFile(dmgFile)
            // 盖章
            stapleMacOSLocalFile(dmgFile)
        }.apply { jobs.add(this) }

        // join ...
        jobs.forEach { it.get() }

        // shutdown
        pool.shutdown()
    }

}

/**
 * 创建 tar.gz 和 AppImage
 */
fun packOnLinux(distributionDir: Directory, finalFilenameWithoutExtension: String, projectName: String) {

    if (isDeb) {
        val arch = if (arch.isArm) "arm" else "amd"
        distributionDir.file("${project.name}_${appVersion}_${arch}64.deb").asFile
            .renameTo(distributionDir.file("${finalFilenameWithoutExtension}.deb").asFile)
        return
    }

    val cfg = FileUtils.getFile(distributionDir.asFile, projectName, "lib", "app", "${projectName}.cfg")
    val configText = cfg.readText()

    // tar.gz
    cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=tar.gz").toString())
    exec {
        commandLine(
            "tar", "-czvf",
            distributionDir.file("${finalFilenameWithoutExtension}.tar.gz").asFile.absolutePath,
            projectName
        )
        workingDir = distributionDir.asFile
    }


    // AppImage
    // Download AppImageKit
    val appimagetool = FileUtils.getFile(projectDir, ".gradle", "appimagetool")
    if (!appimagetool.exists()) {
        exec {
            commandLine(
                "wget",
                "-O", appimagetool.absolutePath,
                "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${if (arch.isArm) "aarch64" else "x86_64"}.AppImage"
            )
            workingDir = distributionDir.asFile
        }

        // AppImageKit chmod
        exec { commandLine("chmod", "+x", appimagetool.absolutePath) }
    }

    // Desktop file
    val termoraName = project.name.uppercaseFirstChar()

    // copy icon
    FileUtils.copyFile(
        File("${projectDir.absolutePath}/src/main/resources/icons/termora_256x256.png"),
        distributionDir.file(termoraName + File.separator + termoraName + ".png").asFile
    )

    val desktopFile = distributionDir.file(termoraName + File.separator + termoraName + ".desktop").asFile
    desktopFile.writeText(
        """[Desktop Entry]
Type=Application
Name=${termoraName}
Comment=Terminal emulator and SSH client
Icon=${termoraName}
Categories=Development;
StartupWMClass=${termoraName}
Terminal=false
""".trimIndent()
    )

    // AppRun file
    val appRun = File(desktopFile.parentFile, "AppRun")
    val sb = StringBuilder()
    sb.append("#!/bin/sh").appendLine()
    sb.append("SELF=$(readlink -f \"$0\")").appendLine()
    sb.append("HERE=\${SELF%/*}").appendLine()
    sb.append("export LinuxAppImage=true").appendLine()
    sb.append("exec \"\${HERE}/bin/${termoraName}\" \"$@\"")
    appRun.writeText(sb.toString())
    appRun.setExecutable(true)

    // AppImage
    cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=AppImage").toString())
    exec {
        commandLine(appimagetool.absolutePath, termoraName, "${finalFilenameWithoutExtension}.AppImage")
        workingDir = distributionDir.asFile
    }
}

/**
 * macOS 对本地文件进行签名
 */
fun signMacOSLocalFile(file: File) {
    if (os.isMacOsX && macOSSign) {
        if (file.exists() && file.isFile) {
            exec {
                commandLine(
                    "/usr/bin/codesign",
                    "-s", macOSSignUsername,
                    "--timestamp", "--force",
                    "-vvvv", "--options", "runtime",
                    file.absolutePath,
                )
            }
        }
    }
}

/**
 * macOS 对本地文件进行公证
 */
fun notaryMacOSLocalFile(file: File) {
    if (os.isMacOsX && macOSNotary) {
        if (file.exists()) {
            exec {
                commandLine(
                    "/usr/bin/xcrun", "notarytool",
                    "submit", file,
                    "--keychain-profile", macOSNotaryKeychainProfile,
                    "--wait",
                )
            }
        }
    }
}

/**
 * 盖章
 */
fun stapleMacOSLocalFile(file: File) {
    if (os.isMacOsX && macOSNotary) {
        if (file.exists()) {
            exec {
                commandLine(
                    "/usr/bin/xcrun",
                    "stapler", "staple", file,
                )
            }
        }
    }
}


kotlin {
    jvmToolchain {
        languageVersion = JavaLanguageVersion.of(25)
    }
}

java {
    withSourcesJar()
}

idea {
    module {
        isDownloadJavadoc = true
        isDownloadSources = true
    }
}
