plugins {
    alias(libs.plugins.android.application)
    // kotlin-android NOT applied: AGP 9 enables built-in Kotlin by default,
    // and KSP 2.3.x supports it (PR #2674, merged Oct 2025). serialization
    // + compose-compiler are language-level Kotlin compiler plugins and
    // still need explicit application.
    alias(libs.plugins.kotlin.serialization)
    alias(libs.plugins.compose.compiler)
    alias(libs.plugins.ksp)
    alias(libs.plugins.androidx.room)
    alias(libs.plugins.hilt)
    alias(libs.plugins.ktlint)
    alias(libs.plugins.detekt)
}

android {
    namespace = "com.fabledsword.minstrel"
    compileSdk = 36

    defaultConfig {
        applicationId = "com.fabledsword.minstrel"
        minSdk = 26
        targetSdk = 36
        // versionName / versionCode are released-build values injected by
        // CI from the git tag + commit count. Local / debug builds fall
        // back to "dev" so the About card reads honestly. Releases ship
        // versionName="YYYY.MM.DD.<commits>" (e.g. "2026.06.02.142") and
        // versionCode=<commits>, which is monotonic forever and lets the
        // shared isVersionNewer comparator distinguish two same-day
        // re-cuts (the iteration suffix differs).
        val versionNameOverride =
            (project.findProperty("MINSTREL_VERSION_NAME") as String?)?.takeIf { it.isNotBlank() }
        val versionCodeOverride =
            (project.findProperty("MINSTREL_VERSION_CODE") as String?)?.toIntOrNull()
        versionCode = versionCodeOverride ?: 1
        versionName = versionNameOverride ?: "dev"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables { useSupportLibrary = true }
    }

    // Room schema export is handled by the androidx.room Gradle plugin via
    // the room {} block below — replaces the legacy
    // `ksp { arg("room.schemaLocation", ...) }` pattern in Room 2.7+.

    signingConfigs {
        create("release") {
            val keystorePath: String? = System.getenv("ANDROID_KEYSTORE_PATH")
            if (!keystorePath.isNullOrEmpty()) {
                storeFile = file(keystorePath)
                storePassword = System.getenv("ANDROID_STORE_PASSWORD")
                keyAlias = System.getenv("ANDROID_KEY_ALIAS")
                keyPassword = System.getenv("ANDROID_KEY_PASSWORD")
            }
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro",
            )
            signingConfig =
                if (System.getenv("ANDROID_KEYSTORE_PATH").isNullOrEmpty()) {
                    signingConfigs.getByName("debug")
                } else {
                    signingConfigs.getByName("release")
                }
        }
    }

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

    buildFeatures {
        compose = true
        buildConfig = true
    }

    packaging {
        resources.excludes +=
            setOf(
                "/META-INF/{AL2.0,LGPL2.1}",
                "META-INF/LICENSE.md",
                "META-INF/LICENSE-notice.md",
            )
    }
}

// Kotlin 2.x: `kotlinOptions { ... }` inside `android { }` is gone; the
// modern shape is the top-level `kotlin { compilerOptions { ... } }` block,
// which works whether Kotlin comes from AGP 9's built-in path or an
// explicit plugin alias.
kotlin {
    compilerOptions {
        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
        // Opt into the future Kotlin behavior: annotations on
        // constructor parameters apply to both the param AND the
        // generated property/field. Without this flag, Kotlin 2.x
        // emits a deprecation warning at every @Inject /
        // @ApplicationContext / @ApplicationScope constructor-
        // parameter use. Setting it now matches what becomes the
        // default in Kotlin 2.3 (KT-73255).
        freeCompilerArgs.add("-Xannotation-default-target=param-property")
    }
}

// Room schema export — generated JSON dumps live under android/app/schemas/
// for migration-test fixtures. Replaces the legacy
// `ksp { arg("room.schemaLocation", ...) }` arg-passing.
room {
    schemaDirectory("$projectDir/schemas")
}

detekt {
    toolVersion = libs.versions.detekt.get()
    config.setFrom(files("$rootDir/config/detekt.yml"))
    buildUponDefaultConfig = true
    parallel = true
    // `autoCorrect` was dropped in detekt 2.0's DSL options list.
}

// detekt 2.0 moved its task types to the `dev.detekt.gradle` package and
// flipped `jvmTarget` to the Property API. Pin to 17 to match our actual
// bytecode target (compileOptions.targetCompatibility +
// kotlin.compilerOptions.jvmTarget).
tasks.withType<dev.detekt.gradle.Detekt>().configureEach {
    jvmTarget.set("17")
}
tasks.withType<dev.detekt.gradle.DetektCreateBaselineTask>().configureEach {
    jvmTarget.set("17")
}

dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.compose)
    implementation(libs.androidx.lifecycle.viewmodel.compose)
    implementation(libs.androidx.lifecycle.process)
    implementation(libs.androidx.activity.compose)
    implementation(libs.androidx.nav.compose)
    implementation(libs.androidx.hilt.nav.compose)
    implementation(libs.androidx.hilt.work)
    ksp(libs.androidx.hilt.compiler)
    implementation(libs.androidx.work.runtime.ktx)

    implementation(platform(libs.compose.bom))
    implementation(libs.compose.ui)
    implementation(libs.compose.ui.graphics)
    implementation(libs.compose.material3)
    implementation(libs.compose.ui.text.google.fonts)
    debugImplementation(libs.compose.ui.tooling)
    implementation(libs.compose.ui.tooling.preview)

    implementation(libs.hilt.android)
    ksp(libs.hilt.compiler)

    implementation(libs.room.runtime)
    implementation(libs.room.ktx)
    ksp(libs.room.compiler)

    implementation(libs.retrofit)
    implementation(libs.retrofit.kotlinx.serialization.converter)
    implementation(libs.okhttp)
    implementation(libs.okhttp.logging)
    implementation(libs.okhttp.sse)
    implementation(libs.kotlinx.serialization.json)
    implementation(libs.kotlinx.coroutines.android)
    implementation(libs.kotlinx.datetime)

    implementation(libs.media3.exoplayer)
    implementation(libs.media3.session)
    implementation(libs.media3.datasource.okhttp)
    implementation(libs.mediarouter)

    implementation(libs.coil.compose)
    implementation(libs.coil.network.okhttp)
    implementation(libs.androidx.palette)
    implementation(libs.icons.lucide)

    implementation(libs.timber)

    testImplementation(libs.junit.jupiter)
    testImplementation(libs.turbine)
    testImplementation(libs.mockk)
    testImplementation(libs.kotlinx.coroutines.test)
    testImplementation(libs.okhttp.mockwebserver)
    // kxml2 — provides an org.xmlpull.v1 impl on the JVM unit-test
    // classpath. Android's stock XmlPullParserFactory resolves to the
    // android.jar Stub on JVM tests; kxml2 is picked up via service-
    // provider lookup and makes XmlPullParserFactory.newInstance() work
    // unconditionally so DeviceDescriptionTest runs in CI.
    testImplementation(libs.kxml2)
    // kotlin.test for assertEquals/assertNull/etc. — version managed by
    // the applied Kotlin plugin so no explicit version pin needed.
    testImplementation(kotlin("test"))
    // Gradle 9 no longer auto-injects the JUnit Platform launcher; must
    // be declared explicitly on the runtime classpath for useJUnitPlatform()
    // to discover tests.
    testRuntimeOnly(libs.junit.platform.launcher)

    androidTestImplementation(platform(libs.compose.bom))
    androidTestImplementation(libs.compose.ui.test)
    // androidTest dep parity with the unit-test side; needed once we have
    // instrumented tests that consume the same APIs.
    androidTestImplementation(kotlin("test"))
    androidTestImplementation(libs.kotlinx.coroutines.test)
    debugImplementation(libs.compose.ui.test.manifest)
}

tasks.withType<Test> {
    useJUnitPlatform()
    // Print the assertion message + full stack trace for failures. The
    // default console output gives only "AssertionError at Foo.kt:12", and
    // for a failure inside a `runTest { }` lambda even that line collapses
    // to the test function's own line (the assertion frames live in the
    // suspend-lambda class, which Gradle filters out) — leaving nothing to
    // debug from when the HTML report isn't reachable, as in CI.
    testLogging {
        events("failed")
        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
        showStackTraces = true
    }
}
