package com.meowarex.rlmobile.manager

import android.annotation.SuppressLint
import android.app.Application
import android.os.Build
import android.os.storage.StorageManager
import android.util.Log
import androidx.compose.runtime.Immutable
import androidx.core.content.getSystemService
import com.meowarex.rlmobile.BuildConfig
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
import com.meowarex.rlmobile.util.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.time.Duration
import kotlin.time.Instant

/**
 * In-memory store for the most recent install attempts
 */
class InstallLogManager(
    private val application: Application,
    private val prefs: PreferencesManager,
    @Suppress("unused") private val json: Json,
) {
    private val entries = linkedMapOf<String, InstallLogData>()

    fun fetchInstallData(id: String): InstallLogData? = synchronized(entries) {
        entries[id]
    }

    suspend fun storeInstallData(
        id: String,
        installDate: Instant,
        installDuration: Duration,
        options: PatchOptions,
        log: String,
        error: Throwable?,
    ) {
        val data = InstallLogData(
            id = id,
            installDate = installDate,
            installDuration = installDuration,
            installOptions = options,
            environmentInfo = getEnvironmentInfo(),
            installationLog = log,
            errorStacktrace = error?.let { Log.getStackTraceString(it).trimEnd() },
        )

        synchronized(entries) {
            entries[id] = data
            // Keep only the most recent few in memory.
            while (entries.size > MAX_ENTRIES) {
                val oldest = entries.keys.iterator().next()
                entries.remove(oldest)
            }
        }
    }

    @Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
    @SuppressLint("UsableSpace")
    suspend fun getEnvironmentInfo(): String {
        val storageManager = application.getSystemService<StorageManager>()!!

        val buildType = when {
            BuildConfig.RELEASE -> "(Release)"
            BuildConfig.GIT_LOCAL_CHANGES || BuildConfig.GIT_LOCAL_COMMITS -> "(Changes present)"
            else -> ""
        }
        val soc = if (Build.VERSION.SDK_INT >= 31) (Build.SOC_MANUFACTURER + ' ' + Build.SOC_MODEL) else "Unavailable"
        val playProtect = when (application.isPlayProtectEnabled()) {
            null -> "Unavailable"
            true -> "Enabled"
            false -> "Disabled"
        }

        val diskFreeSize = application.filesDir.usableSpace
        val cacheQuotaSize = if (Build.VERSION.SDK_INT >= 26) {
            storageManager.getCacheQuotaBytes(storageManager.getUuidForPath(application.cacheDir))
        } else {
            0L
        }

        return """
            Radiant Lyrics Manager v${BuildConfig.VERSION_NAME}
            Built from commit ${BuildConfig.GIT_COMMIT} on ${BuildConfig.GIT_BRANCH} $buildType
            Developer mode: ${if (prefs.devMode) "On" else "Off"}
            External storage: ${if (prefs.devMode || prefs.keepPatchedApks) "Yes" else "No"}

            Disk Free: ${diskFreeSize.formatShortFileSize()}
            Cache Quota: ${cacheQuotaSize.formatShortFileSize()}

            Android API: ${Build.VERSION.SDK_INT}
            Supported ABIs: ${Build.SUPPORTED_ABIS.joinToString()}
            ROM: Android ${Build.VERSION.RELEASE} (Patch ${Build.VERSION.SECURITY_PATCH})
            Device: ${Build.MANUFACTURER} ${Build.MODEL} (${Build.DEVICE})
            Emulator: ${if (IS_PROBABLY_EMULATOR) "Yes" else "No"} (guess)
            Play Protect: $playProtect
            SOC: $soc
        """.trimIndent()
    }

    private companion object {
        const val MAX_ENTRIES = 8
    }
}

@Immutable
@Serializable
data class InstallLogData(
    val id: String,
    val installDate: Instant,
    val installDuration: Duration,
    val installOptions: PatchOptions,
    val environmentInfo: String,
    val installationLog: String,
    val errorStacktrace: String?,
) {
    val isError: Boolean
        get() = errorStacktrace != null

    fun getFormattedInstallDate(): String {
        @SuppressLint("SimpleDateFormat")
        return SimpleDateFormat("yyyy-MM-dd HH-mm-ss", Locale.ENGLISH)
            .format(Date(installDate.toEpochMilliseconds()))
    }

    fun getLogFileContents(): String = buildString {
        appendLine("////////////////// Environment Info //////////////////")
        appendLine(environmentInfo)

        append("\n\n")
        appendLine("////////////////// Installation Info //////////////////")
        appendLine()
        append("Install ID: ")
        appendLine(id)
        append("Install time: ")
        appendLine(getFormattedInstallDate())
        append("Result: ")
        appendLine(if (isError) "Failure" else "Success")

        append("\n\n")
        appendLine("////////////////// Error Stacktrace //////////////////")
        appendLine()
        appendLine(errorStacktrace ?: "None")

        append("\n\n")
        appendLine("////////////////// Installation Log //////////////////")
        appendLine()
        appendLine(installationLog)
    }
}
