import java.net.URI
import java.nio.file.Files
import java.nio.file.Path
import org.gradle.api.tasks.compile.JavaCompile

plugins {
    id("com.android.application")
}

val releaseBaseUrl = providers.gradleProperty("WNH_BASE_URL").orElse("")
val debugBaseUrl = providers.gradleProperty("WNH_DEBUG_BASE_URL").orElse("")
val trackingMinTimeMs = providers.gradleProperty("WNH_TRACKING_MIN_TIME_MS").orElse("0")
val trackingHttpTimeoutMs =
    providers.gradleProperty("WNH_TRACKING_HTTP_TIMEOUT_MS").orElse("0")
val instrumentationBuildType =
    providers.gradleProperty("WNH_TEST_BUILD_TYPE").orElse("debug")
val androidVersionCode = providers.gradleProperty("WNH_ANDROID_VERSION_CODE").orElse("1")
val androidVersionName = providers.gradleProperty("WNH_ANDROID_VERSION_NAME").orElse("0.1.0")
val releaseSigningStoreFile =
    providers.environmentVariable("WNH_ANDROID_SIGNING_STORE_FILE").orNull
val releaseSigningPasswordFile =
    providers.environmentVariable("WNH_ANDROID_SIGNING_PASSWORD_FILE").orNull
val releaseSigningKeyAlias =
    providers.environmentVariable("WNH_ANDROID_SIGNING_KEY_ALIAS").orNull

fun nonBlank(value: String?): String? = value?.trim()?.takeIf(String::isNotEmpty)

val releaseSigningInputs =
    listOf(
        nonBlank(releaseSigningStoreFile),
        nonBlank(releaseSigningPasswordFile),
        nonBlank(releaseSigningKeyAlias)
    )
val releaseSigningConfigured = releaseSigningInputs.all { it != null }
val releaseSigningPartiallyConfigured = releaseSigningInputs.any { it != null }

fun readSigningPassword(): String {
    val passwordPath =
        nonBlank(releaseSigningPasswordFile)
            ?: throw GradleException("Android release signing password file is not configured")
    val path = Path.of(passwordPath)

    if (!Files.isRegularFile(path)) {
        throw GradleException("Android release signing password file does not exist")
    }

    val password = Files.readString(path).trimEnd('\r', '\n')
    if (password.isBlank() || password.contains('\n') || password.contains('\r')) {
        throw GradleException("Android release signing password file is invalid")
    }

    return password
}

fun manifestOrigin(value: String): URI? =
    runCatching { URI(value) }
        .getOrNull()
        ?.takeIf { uri ->
            (uri.scheme == "http" || uri.scheme == "https") && !uri.host.isNullOrBlank()
        }

fun isOriginPath(uri: URI): Boolean =
    uri.path.isNullOrEmpty() || uri.path == "/"

val debugManifestOrigin = manifestOrigin(debugBaseUrl.get())
val releaseManifestOrigin = manifestOrigin(releaseBaseUrl.get())

android {
    namespace = "org.whoneedhelp.mobile"
    compileSdk = 37
    buildToolsVersion = "37.0.0"
    testBuildType = instrumentationBuildType.get()

    defaultConfig {
        applicationId = "org.whoneedhelp.mobile"
        minSdk = 24
        targetSdk = 37
        versionCode =
            androidVersionCode.get().toIntOrNull()?.takeIf { it > 0 }
                ?: throw GradleException("WNH_ANDROID_VERSION_CODE must be a positive integer")
        versionName =
            androidVersionName.get().trim().takeIf(String::isNotEmpty)
                ?: throw GradleException("WNH_ANDROID_VERSION_NAME must not be empty")
        buildConfigField("long", "TRACKING_MIN_TIME_MS", "${trackingMinTimeMs.get()}L")
        buildConfigField(
            "long",
            "TRACKING_HTTP_TIMEOUT_MS",
            "${trackingHttpTimeoutMs.get()}L"
        )

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    signingConfigs {
        if (releaseSigningConfigured) {
            create("release") {
                val storePath = Path.of(nonBlank(releaseSigningStoreFile)!!)
                if (!Files.isRegularFile(storePath)) {
                    throw GradleException("Android release signing keystore does not exist")
                }

                storeFile = storePath.toFile()
                storePassword = readSigningPassword()
                keyAlias = nonBlank(releaseSigningKeyAlias)
                keyPassword = storePassword
            }
        }
    }

    buildTypes {
        debug {
            applicationIdSuffix = ".debug"
            versionNameSuffix = "-debug"
            buildConfigField(
                "String",
                "BASE_URL",
                "\"${debugBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
            )
            manifestPlaceholders["usesCleartextTraffic"] = "true"
            manifestPlaceholders["deepLinkScheme"] = debugManifestOrigin?.scheme ?: "https"
            manifestPlaceholders["deepLinkHost"] =
                debugManifestOrigin?.host ?: "invalid.whoneedhelp.local"
        }

        create("staging") {
            initWith(getByName("debug"))
            applicationIdSuffix = ".staging"
            versionNameSuffix = "-staging"
            buildConfigField(
                "String",
                "BASE_URL",
                "\"${releaseBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
            )
            manifestPlaceholders["usesCleartextTraffic"] = "false"
            manifestPlaceholders["deepLinkScheme"] = releaseManifestOrigin?.scheme ?: "https"
            manifestPlaceholders["deepLinkHost"] =
                releaseManifestOrigin?.host ?: "invalid.whoneedhelp.local"
            matchingFallbacks += listOf("debug")
        }

        release {
            isMinifyEnabled = true
            isShrinkResources = true
            if (releaseSigningConfigured) {
                signingConfig = signingConfigs.getByName("release")
            }
            buildConfigField(
                "String",
                "BASE_URL",
                "\"${releaseBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
            )
            manifestPlaceholders["usesCleartextTraffic"] = "false"
            manifestPlaceholders["deepLinkScheme"] = releaseManifestOrigin?.scheme ?: "https"
            manifestPlaceholders["deepLinkHost"] =
                releaseManifestOrigin?.host ?: "invalid.whoneedhelp.local"
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }

    buildFeatures {
        buildConfig = true
    }

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

    testOptions {
        unitTests.isIncludeAndroidResources = false
    }
}

tasks.matching { it.name == "preReleaseBuild" || it.name == "preStagingBuild" }.configureEach {
    doFirst {
        if (name == "preReleaseBuild" && !releaseSigningConfigured) {
            val detail =
                if (releaseSigningPartiallyConfigured) {
                    "Release signing is only partially configured"
                } else {
                    "Release signing is not configured"
                }
            throw GradleException(
                "$detail; set WNH_ANDROID_SIGNING_STORE_FILE, "
                    + "WNH_ANDROID_SIGNING_PASSWORD_FILE, and "
                    + "WNH_ANDROID_SIGNING_KEY_ALIAS"
            )
        }

        val value = releaseBaseUrl.orNull.orEmpty()
        val uri = runCatching { URI(value) }.getOrNull()

        if (
            uri == null ||
            uri.scheme != "https" ||
            uri.host.isNullOrBlank() ||
            uri.userInfo != null ||
            !isOriginPath(uri) ||
            uri.query != null ||
            uri.fragment != null
        ) {
            throw GradleException(
                "Staging and release builds require "
                    + "-PWNH_BASE_URL=https://your-real-deployment.example"
            )
        }

        validateTrackingConfiguration()
    }
}

tasks.matching { it.name == "preDebugBuild" }.configureEach {
    doFirst {
        val value = debugBaseUrl.orNull.orEmpty()
        val uri = runCatching { URI(value) }.getOrNull()

        if (
            uri == null ||
            (uri.scheme != "http" && uri.scheme != "https") ||
            uri.host.isNullOrBlank() ||
            uri.userInfo != null ||
            !isOriginPath(uri) ||
            uri.query != null ||
            uri.fragment != null
        ) {
            throw GradleException(
                "Debug builds require -PWNH_DEBUG_BASE_URL=http(s)://your-development-host"
            )
        }

        validateTrackingConfiguration()
    }
}

fun validateTrackingConfiguration() {
    val minTime = trackingMinTimeMs.orNull?.toLongOrNull()
    val httpTimeout = trackingHttpTimeoutMs.orNull?.toLongOrNull()

    if (minTime == null || minTime <= 0) {
        throw GradleException(
            "Builds require -PWNH_TRACKING_MIN_TIME_MS=POSITIVE_MILLISECONDS"
        )
    }

    if (httpTimeout == null || httpTimeout <= 0 || httpTimeout > Int.MAX_VALUE) {
        throw GradleException(
            "Builds require -PWNH_TRACKING_HTTP_TIMEOUT_MS=POSITIVE_INT_MILLISECONDS"
        )
    }
}

tasks.withType<JavaCompile>().configureEach {
    options.compilerArgs.add("-Xlint:deprecation")
}

dependencies {
    implementation("androidx.activity:activity:1.13.0")
    implementation("androidx.webkit:webkit:1.16.0")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test:core:1.7.0")
    androidTestImplementation("androidx.test:runner:1.7.0")
    androidTestImplementation("androidx.test:rules:1.7.0")
    androidTestImplementation("androidx.test.ext:junit:1.3.0")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
    androidTestImplementation("androidx.test.espresso:espresso-web:3.7.0")
    androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0")
}
