plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.compose)
    alias(libs.plugins.hilt)
    alias(libs.plugins.kotlin.serialization)
    alias(libs.plugins.ksp)
    alias(libs.plugins.ktlint)
}

fun Project.gitOutputOrNull(vararg args: String): String? =
    runCatching {
        val execOutput =
            providers.exec {
                commandLine("git", *args)
            }
        val result = execOutput.result.get()
        if (result.exitValue == 0) {
            execOutput.standardOutput.asText
                .get()
                .trim()
                .ifBlank { null }
        } else {
            null
        }
    }.getOrNull()

val gitShortSha = project.gitOutputOrNull("rev-parse", "--short", "HEAD")
val gitCommitCount = project.gitOutputOrNull("rev-list", "--count", "HEAD")?.toIntOrNull()

val rawVersionNameProp = project.findProperty("VERSION_NAME") as String?
val rawVersionCodeProp = (project.findProperty("VERSION_CODE") as String?)?.toIntOrNull()
val useGitVersionFallback =
    rawVersionNameProp == null ||
        rawVersionNameProp == "0.1.0" ||
        rawVersionCodeProp == null ||
        rawVersionCodeProp == 1

val versionNameProp =
    if (useGitVersionFallback) {
        "0.1.0-dev${gitShortSha?.let { "+$it" } ?: ""}"
    } else {
        rawVersionNameProp!!
    }
val versionCodeProp =
    if (useGitVersionFallback) {
        gitCommitCount ?: 1
    } else {
        rawVersionCodeProp!!
    }

android {
    namespace = "com.memohai.autofish"
    compileSdk = 36

    defaultConfig {
        applicationId = "com.memohai.autofish"
        minSdk = 33
        targetSdk = 34
        versionCode = versionCodeProp
        versionName = versionNameProp
    }

    buildTypes {
        debug {
            applicationIdSuffix = ".debug"
            isDebuggable = true
            isMinifyEnabled = false
        }
        release {
            isDebuggable = false
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro",
            )
        }
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    buildFeatures {
        buildConfig = true
        compose = true
    }

    testOptions {
        unitTests.isReturnDefaultValues = true
    }

    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
            excludes += "/META-INF/INDEX.LIST"
            excludes += "/META-INF/io.netty.*"
            excludes += "/META-INF/LICENSE.md"
            excludes += "/META-INF/LICENSE-notice.md"
        }
        jniLibs {
            keepDebugSymbols += "**/libandroidx.graphics.path.so"
            keepDebugSymbols += "**/libdatastore_shared_counter.so"
        }
    }
}

androidComponents {
    onVariants { variant ->
        variant.outputs.forEach { output ->
            output.outputFileName.set("auto-fish-$versionNameProp-${variant.buildType}.apk")
        }
    }
}

kotlin {
    compilerOptions {
        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
    }
}

val detektCli by configurations.creating {
    isCanBeConsumed = false
    isCanBeResolved = true
}

dependencies {
    // AndroidX Core
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.activity.compose)

    // Material Components (XML themes)
    implementation(libs.material)

    // Compose
    implementation(platform(libs.compose.bom))
    implementation(libs.compose.ui)
    implementation(libs.compose.ui.graphics)
    implementation(libs.compose.ui.tooling.preview)
    implementation(libs.compose.material3)
    implementation(libs.compose.material.icons.extended)
    debugImplementation(libs.compose.ui.tooling)

    // Lifecycle
    implementation(libs.lifecycle.runtime.ktx)
    implementation(libs.lifecycle.viewmodel.compose)
    implementation(libs.lifecycle.runtime.compose)

    // DataStore
    implementation(libs.datastore.preferences)

    // Ktor Server
    implementation(libs.ktor.server.core)
    implementation(libs.ktor.server.netty)
    implementation(libs.ktor.server.content.negotiation)
    implementation(libs.ktor.serialization.kotlinx.json)

    runtimeOnly(libs.slf4j.android)

    // Kotlinx
    implementation(libs.kotlinx.serialization.json)
    implementation(libs.kotlinx.coroutines.core)
    implementation(libs.kotlinx.coroutines.android)

    // Hilt
    implementation(libs.hilt.android)
    implementation(libs.hilt.navigation.compose)
    ksp(libs.hilt.compiler)

    // Shizuku
    implementation(libs.shizuku.api)
    implementation(libs.shizuku.provider)

    // Unit Testing
    testImplementation(platform(libs.junit.bom))
    testImplementation(libs.junit.jupiter.api)
    testImplementation(libs.junit.jupiter.params)
    testRuntimeOnly(libs.junit.jupiter.engine)
    testRuntimeOnly(libs.junit.platform.launcher)
    testImplementation(libs.mockk)
    testImplementation(libs.turbine)
    testImplementation(libs.kotlinx.coroutines.test)
    testImplementation(libs.ktor.server.test.host)
    testImplementation(libs.ktor.client.content.negotiation)
    testImplementation(libs.ktor.sse)

    detektCli(libs.detekt.cli)
}

tasks.withType<Test> {
    useJUnitPlatform()
    maxHeapSize = "4g"
    maxParallelForks = (Runtime.getRuntime().availableProcessors()).coerceAtLeast(1)
    jvmArgs(
        "--add-opens",
        "java.base/java.lang=ALL-UNNAMED",
        "--add-opens",
        "java.base/java.lang.reflect=ALL-UNNAMED",
        "--add-opens",
        "java.base/java.util=ALL-UNNAMED",
        "--add-opens",
        "java.base/java.time=ALL-UNNAMED",
    )
}

val detektInputDirs =
    files(
        "src/main/kotlin",
        "src/test/kotlin",
    )
val detektReportDir = layout.buildDirectory.dir("reports/detekt")

tasks.register<JavaExec>("detekt") {
    group = "verification"
    description = "Run detekt static analysis."
    classpath = detektCli
    mainClass.set("io.gitlab.arturbosch.detekt.cli.Main")

    inputs.files(detektInputDirs)
    outputs.dir(detektReportDir)

    val reportDir = detektReportDir.get().asFile
    doFirst {
        reportDir.mkdirs()
    }
    args(
        "--input",
        detektInputDirs.files.joinToString(",") { it.absolutePath },
        "--report",
        "md:${reportDir.resolve("detekt.md").absolutePath}",
        "--report",
        "html:${reportDir.resolve("detekt.html").absolutePath}",
        "--report",
        "xml:${reportDir.resolve("detekt.xml").absolutePath}",
        "--report",
        "sarif:${reportDir.resolve("detekt.sarif").absolutePath}",
    )
}

tasks.named("check") {
    dependsOn("detekt")
}
