@file:OptIn(ExternalKotlinTargetApi::class)

import org.jetbrains.compose.ExperimentalComposeLibrary
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinBinaryCoordinates
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinBinaryDependency
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinClasspath
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.DependencyResolutionPhase
import org.jetbrains.kotlin.gradle.plugin.kotlinToolingVersion
import java.util.Properties

plugins {
    alias(libs.plugins.kotlin.multiplatform)
    alias(libs.plugins.compose.compiler)
    alias(libs.plugins.compose.multiplatform)
    alias(libs.plugins.android.kmp.library)
    alias(libs.plugins.kotlinx.serialization)
    alias(libs.plugins.buildConfig)
}

kotlin {
    applyDefaultHierarchyTemplate()

    jvm("desktop")
    android {
        namespace = "theorycrafter"
        compileSdk = libs.versions.android.compileSdk.get().toInt()
        minSdk = libs.versions.android.minSdk.get().toInt()
        androidResources.enable = true
        compilerOptions { jvmTarget.set(JvmTarget.JVM_21) }
    }

    sourceSets {
        val commonMain by getting
        val jvmMain by creating
        val desktopMain by getting
        val androidMain by getting

        jvmMain.dependsOn(commonMain)
        androidMain.dependsOn(jvmMain)
        desktopMain.dependsOn(jvmMain)

        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.ui)
            implementation(compose.foundation)
            implementation(compose.material)
            implementation(compose.components.resources)
            implementation(compose.components.uiToolingPreview)
            implementation(compose.material)
            implementation(compose.materialIconsExtended)
            implementation(libs.kotlinx.serialization.json)

            implementation(":EveData")
            implementation(":Esi")
            implementation(":FittingEngine")
            implementation(":ComposeWidgets")
        }

        jvmMain.dependencies {
            // These shouldn't be needed; coroutines should come from compose and okhttp from Esi,
            // but IDEA doesn't resolve them otherwise
            implementation(libs.kotlinx.coroutines.core.jvm)
            implementation(libs.okhttp)
        }

        desktopMain.dependencies {
            implementation(compose.desktop.currentOs)
            implementation(compose.components.resources)

            // SQLite is needed to access the Pyfa database.
            // Instead of the single jar with the .so files for all the supported platforms, we use a custom
            // jar with just the single .so file for the current platform.
            //    implementation("org.xerial:sqlite-jdbc:3.49.1.0")
            implementation(
                fileTree("desktopLibs") {
                    include(sqliteJdbcJarName())
                }
            )
            implementation(libs.slf4j.api)  // Used by org.xerial:sqlite-jdbc
            implementation(libs.slf4j.nop)  // Prevents a warning being printed by slf4j-api
        }

        commonTest.dependencies {
            implementation(kotlin("test"))
            @OptIn(ExperimentalComposeLibrary::class)
            implementation(compose.uiTest)
        }
    }

    jvmToolchain(21)

    compilerOptions.freeCompilerArgs.apply {
        add("-Xlambdas=indy")
        add("-Xcontext-receivers")
        add("-Xexpect-actual-classes")
    }
}

// To avoid bloating the distribution with all the native JDBC implementations, I removed all but the necessary,
// per platform .so files from the jar in misc/sqlite-jdbc-3.49.1.0-original.jar, and put them in separate jars in
// libs/. This function returns the name of the jar for the running platform.
// The original jar was downloaded from https://github.com/xerial/sqlite-jdbc
fun sqliteJdbcJarName(): String {
    val os = org.gradle.internal.os.OperatingSystem.current()
    val osType = when {
        os.isWindows -> "windows"
        os.isMacOsX -> "macosx"
        os.isLinux -> "linux"
        else -> throw UnsupportedOperationException("$os is not currently supported by the custom sqlite-jdbc")
    }
    val arch = System.getProperty("os.arch")
    val archType = if (arch == "aarch64" || arch == "arm64") "aarch64" else "x64"
    return "sqlite-jdbc-3.49.1.0-$osType-$archType.jar"
}

// The 3 blocks below make IDEA able to resolve the kotlin-stdlib-jvm in the jvmMain source-set
val jvmStdlib: Configuration = project.configurations.create("jvmStdlib") {
    isCanBeResolved = true
    isCanBeConsumed = false
    attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API)) }
    attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) }
    attributes { attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) }
    attributes { attribute(KotlinPlatformType.attribute, KotlinPlatformType.jvm) }
}

dependencies {
    jvmStdlib(kotlin("stdlib"))
}

IdeMultiplatformImport.instance(project).registerDependencyResolver(
    resolver = {
        setOf(
            IdeaKotlinResolvedBinaryDependency(
                binaryType = IdeaKotlinBinaryDependency.KOTLIN_COMPILE_BINARY_TYPE,
                classpath = IdeaKotlinClasspath(jvmStdlib.files),
                coordinates = IdeaKotlinBinaryCoordinates(
                    "org.jetbrains.kotlin.fake",
                    "fake-kotlin-stdlib-jvm",
                    project.kotlinToolingVersion.toString()
                )
            )
        )
    },
    constraint = { it.name == "jvmMain" },
    phase = DependencyResolutionPhase.PreDependencyResolution
)

compose.resources {
    customDirectory(
        sourceSetName = "desktopMain",
        directoryProvider = provider { layout.projectDirectory.dir("src/desktopMain/composeResources") }
    )
}

// Prevents the Java icon from being shown in the macOS dock when running tests
tasks.withType<Test>().configureEach {
    systemProperty("apple.awt.UIElement", "true")
}

// Load local.properties
val localProperties = Properties().apply {
    val localPropertiesFile = rootProject.file("local.properties")
    if (localPropertiesFile.exists())
        load(localPropertiesFile.inputStream())
}

val appName = rootProject.extra["appName"] as String

buildConfig {
    packageName("theorycrafter")
    className("Theorycrafter")
    useKotlinOutput {
        internalVisibility = false
    }

    // App stuff
    buildConfigField("AppName", appName)
    buildConfigField("Version", expect<String>())
    buildConfigField("MaintainerEmail", "evetheorycrafter@gmail.com")

    sourceSets.named("desktopMain") {
        val version = "1.9.2-ag7"  // Remember to update versionCode, packageVersion and releases.json
        val versionCode = 23
        val releaseType = "stable"  // stable, beta, dev


        buildConfigField("Version", version)
        buildConfigField("VersionCode", versionCode)
        buildConfigField("theorycrafter.ReleaseType", "AppReleaseType", "ReleaseType.byId(\"$releaseType\")")
        buildConfigField("BuildTimestamp", System.currentTimeMillis())
        buildConfigField("ReleasesInfoUrl", "https://theorycrafter.pro/releases.json")
    }

    sourceSets.named("androidMain") {
        val version = "0.0.1"
        buildConfigField("Version", version)
    }

    // EVE SSO
    buildConfigField("EveSsoClientId", localProperties.getProperty("eve.sso.client_id", ""))
    buildConfigField("EveSsoCallbackUrl", localProperties.getProperty("eve.sso.callback_url", ""))

    // Google Analytics
    buildConfigField("GaMeasurementId", localProperties.getProperty("ga.measurement_id", ""))
    buildConfigField("GaApiSecret", localProperties.getProperty("ga.api_secret", ""))
}

compose.desktop {
    application {
        mainClass = "theorycrafter.MainKt"
        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Rpm/*, TargetFormat.AppImage*/)
            packageName = appName
            packageVersion = "1.9.2"

            modules("java.sql")

            macOS {
                iconFile.set(rootProject.file("misc/icon/mac/theorycrafter.icns"))
                jvmArgs += "-Dapple.awt.application.appearance=system"
            }
            windows {
                menuGroup = "" // This adds a launcher to the start menu directly (without a group)
                iconFile.set(rootProject.file("misc/icon/win/theorycrafter.ico"))
            }
            linux {
                iconFile.set(rootProject.file("misc/icon/linux/theorycrafter.png"))
            }
        }

        buildTypes.release.proguard {
            configurationFiles.from(
                project.file("proguard_rules/okhttp3.pro"),
                project.file("proguard_rules/theorycrafter.pro"),
            )
        }
    }
}