import org.jetbrains.kotlin.gradle.dsl.JvmTarget

// `:commons` is the HEADLESS half of the shared layer: domain models, state
// holders, ViewModels, the relay client, services. It is consumed by every
// front end including the headless `:cli`, so it must never depend on Compose
// UI (ui / foundation / material3), Coil, Compose resources or Skiko — those
// live in `:commonsUI`, which sits on top of this module. Only the Compose
// *runtime* (@Stable/@Immutable + snapshot state) is allowed here.
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidKotlinMultiplatformLibrary)
    // Kept on purpose even though no @Composable lives here anymore: the
    // Compose compiler stamps @StabilityInferred on every class it compiles,
    // which is what lets the apps' composables treat unannotated commons
    // classes (TopFilter, TorSettings, ProfileBroadcastStatus, …) as stable.
    // Measured with Compose compiler reports on full recompiles (2026-09-12):
    // removing this plugin turns 20→28 composable params unstable in
    // :commonsUI, 33→65 in :desktopApp and 90→149 in :amethyst. Inference is
    // also self-maintaining, unlike hand-written @Immutable annotations that
    // silently lie once a `var` is added — so this stays.
    alias(libs.plugins.jetbrainsComposeCompiler)
    alias(libs.plugins.serialization)
}

kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xexpect-actual-classes")
    }

    jvm {
        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_21)
        }
    }

    android {
        namespace = "com.vitorpamplona.amethyst.commons"
        compileSdk =
            libs.versions.android.compileSdk
                .get()
                .toInt()
        minSdk =
            libs.versions.android.minSdk
                .get()
                .toInt()

        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_21)
        }

        androidResources.enable = true

        withHostTest {
            isReturnDefaultValues = true
        }

        withDeviceTest {
            instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        }
    }

    // iOS targets — Phase 2 spike. Compile-only for now (no framework binary
    // configured yet). Reveals which transitive deps need iOS variants and
    // which commonMain files still reach for platform-only APIs.
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        commonMain {
            dependencies {
                implementation(project(":quartz"))

                // Compose *runtime* only — @Stable/@Immutable annotations and
                // snapshot state (mutableStateOf, State) used by state holders.
                // No ui / foundation / material3 here: that is :commonsUI.
                implementation(libs.jetbrains.compose.runtime)

                // Lifecycle ViewModel (KMP since 2.8.0, ships iOS variants).
                // The Compose-side helpers (lifecycle-runtime-compose,
                // viewModel()) live in :commonsUI.
                implementation(libs.androidx.lifecycle.viewmodel)

                // LruCache (KMP-ready)
                implementation(libs.androidx.collection)

                // okio (KMP, Apache-2.0) for service/image/DeferredDeleteFileSystem,
                // the ForwardingFileSystem the apps wrap Coil's disk cache in. It
                // used to arrive transitively through Coil; with Coil in
                // :commonsUI the Apple targets lost it (JVM still saw it via
                // OkHttp), so declare the dependency the file actually has.
                implementation(libs.okio)

                // DataStore (KMP, Apache-2.0) — the preference storage layer.
                // Publishes android/jvm/ios/linux/macos variants plus common
                // metadata, so the stores under model/preferences/ are shared
                // rather than duplicated per front end. Uses the okio-based
                // `createWithPath` factory in common; the `java.io.File`
                // overloads are jvmAndroid-only.
                implementation(libs.androidx.datastore.preferences)

                // Immutable collections
                api(libs.kotlinx.collections.immutable)

                // JSON for custom-feed definitions (KMP — replaces Jackson
                // for the one commonMain serializer that was blocking iOS).
                implementation(libs.kotlinx.serialization.json)
            }
        }

        commonTest {
            dependencies {
                implementation(libs.kotlin.test)
                implementation(libs.kotlinx.coroutines.test)
            }
        }

        // Shared JVM code for both Android and Desktop
        val jvmAndroid =
            create("jvmAndroid") {
                dependsOn(commonMain.get())
                dependencies {
                    // Audio-rooms ViewModel needs the listener orchestration +
                    // audio pipeline types (NestsListener, AudioRoomPlayer,
                    // AudioPlayer interface). The :nestsClient module is
                    // jvmAndroid-only today (its QUIC + Opus + AudioRecord/Track
                    // stacks are JVM-bound), so the dep lives here, not in
                    // commonMain. iOS will need an audio-rooms reroute when
                    // Phase 5 lands.
                    implementation(project(":nestsClient"))

                    // OkHttp (+ coroutines bridge) for the link-preview fetcher
                    // (service/preview/UrlPreview). JVM-only; iOS will swap to
                    // Ktor when its UI ships.
                    implementation(libs.okhttp)
                    implementation(libs.okhttpCoroutines)
                }
            }

        jvmMain {
            dependsOn(jvmAndroid)
            dependencies {
                // Secure key storage via OS keychain (macOS/Windows/Linux)
                implementation(libs.java.keyring)

                // EXIF stripping for image uploads (used by service/upload/MediaCompressor).
                implementation(libs.commons.imaging)

                // Image re-encode + progressive downscale (used by service/upload/ImageReencoder).
                // Pure-Java, MIT. See docs/plans/2026-06-08-feat-desktop-image-compression-plan.md.
                implementation(libs.thumbnailator)

                // Native OS notification bridges — Nucleus per-OS JNI shims.
                // macOS: UNUserNotificationCenter. Windows: WinRT Toasts. Linux: freedesktop D-Bus.
                // Only the matching-OS module's native lib loads at runtime; the others
                // stay dormant on the classpath.
                implementation("io.github.kdroidfilter:nucleus.notification-macos:1.15.7")
                implementation("io.github.kdroidfilter:nucleus.notification-windows:1.15.7")
                implementation("io.github.kdroidfilter:nucleus.notification-linux:1.15.7")
            }
        }

        androidMain {
            dependsOn(jvmAndroid)
            dependencies {
                // androidx.core KTX (Bitmap.scale, prefs.edit {}) used by the
                // Android actuals. Was reaching us transitively through the
                // Compose UI artifacts before the :commonsUI split.
                implementation(libs.androidx.core.ktx)

                // Secure key storage talks to the AndroidKeyStore directly through
                // SecretEncryption; androidx.security.crypto is gone from this module.
            }
        }

        // iOS intermediate so iosArm64Main and iosSimulatorArm64Main share code.
        val iosMain =
            create("iosMain") {
                dependsOn(commonMain.get())
            }
        getByName("iosArm64Main").dependsOn(iosMain)
        getByName("iosSimulatorArm64Main").dependsOn(iosMain)

        getByName("androidHostTest") {
            dependencies {
                implementation(libs.junit)

                // Bitcoin secp256k1 bindings
                implementation(libs.secp256k1.kmp.jni.jvm)
            }
        }

        // jvmTest needs the JVM secp256k1 bindings whenever a test
        // exercises a real signer (e.g. UploadOrchestratorTest, which
        // signs Blossom auth events end-to-end).
        getByName("jvmTest") {
            dependencies {
                implementation(libs.secp256k1.kmp.jni.jvm)
            }
        }

        getByName("androidDeviceTest") {
            dependencies {
                implementation(libs.androidx.junit)
                implementation(libs.androidx.espresso.core)
            }
        }
    }
}

// JVM tests run AWT-backed code (ImageIO, Thumbnailator, BufferedImage) — pin
// headless mode so a stray Toolkit.getDefaultToolkit() in a transitive dep
// never bounces the macOS Dock during CI/local test runs.
tasks.withType<Test>().configureEach {
    if (name == "jvmTest") {
        jvmArgs("-Djava.awt.headless=true")
    }
}

// iOS purity gate — shared task, see gradle/kmp-purity.gradle.kts.
apply(from = rootProject.file("gradle/kmp-purity.gradle.kts"))
