package com.laseroptek.raman.utils


import android.content.Context
import android.net.Uri
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonParser
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapNotNull
import timber.log.Timber
import java.io.InputStreamReader
import java.io.OutputStreamWriter

class PreferenceBackupManager(
    private val context: Context,
    private val dataStore: DataStore<Preferences>
) {
    // A single, pretty-printing Gson instance is all we need.
    private val prettyGson: Gson = GsonBuilder()
        .setPrettyPrinting()
        .create()

    // ----- EXPORT TO JSON -----
    suspend fun exportPreferencesToUri(uri: Uri): Boolean {
        return try {
            // 1. Get the map of all preferences. Values will be native types OR JSON strings.
            val allPrefsMap: Map<String, Any> = dataStore.data
                .mapNotNull { preferences ->
                    preferences.asMap().mapKeys { it.key.name }
                }
                .first()

            if (allPrefsMap.isEmpty()) {
                Timber.d("No preferences to export.")
                return true
            }

            // 2. Iterate through the raw map and build a new map with the correct types.
            val processedMap = mutableMapOf<String, Any>()
            for ((key, value) in allPrefsMap) {
                if (value is String && (value.trim().startsWith("{") || value.trim().startsWith("["))) {
                    // If the value is a String that looks like JSON, parse it.
                    try {
                        // This converts the JSON string into a structured JsonElement (JsonObject or JsonArray)
                        processedMap[key] = JsonParser.parseString(value)
                    } catch (e: Exception) {
                        // If parsing fails, just keep it as a raw string.
                        processedMap[key] = value
                    }
                } else {
                    // For native types (Int, Float, Boolean, etc.), add them directly.
                    processedMap[key] = value
                }
            }

            // 3. Serialize the new, clean map to a pretty-printed JSON string.
            // Gson will correctly handle the JsonElements and native types.
            val jsonString = prettyGson.toJson(processedMap)

            // 4. Write the JSON string to the file.
            context.contentResolver.openOutputStream(uri)?.use { outputStream ->
                OutputStreamWriter(outputStream, "UTF-8").use { writer ->
                    writer.write(jsonString)
                }
            } ?: run {
                Timber.e("Failed to open output stream for URI: $uri")
                return false
            }

            Timber.d("Preferences exported successfully to $uri as JSON")
            true
        } catch (e: Exception) {
            Timber.e(e, "Error exporting preferences to JSON")
            false
        }
    }

    // ----- IMPORT FROM JSON -----
    suspend fun importPreferencesFromUri(uri: Uri, clearExisting: Boolean = true): Boolean {
        return try {
            // 1. Read the JSON string from the file.
            val jsonString = context.contentResolver.openInputStream(uri)?.use {
                InputStreamReader(it, "UTF-8").readText()
            } ?: run {
                Timber.e("Failed to open input stream for URI: $uri")
                return false
            }

            if (jsonString.isBlank()) {
                Timber.w("Import file is empty.")
                return true
            }

            // 2. Deserialize the JSON into a Map<String, Any?>. Gson handles this perfectly.
            val type = object : TypeToken<Map<String, Any?>>() {}.type
            val importedPrefsMap: Map<String, Any?> = prettyGson.fromJson(jsonString, type)

            // 3. Write the imported values back to DataStore.
            dataStore.edit { preferences ->
                if (clearExisting) {
                    preferences.clear()
                }
                for ((keyName, value) in importedPrefsMap) {
                    assignPreferenceValue(preferences, keyName, value)
                }
            }
            Timber.d("Preferences imported successfully from JSON")
            true
        } catch (e: Exception) {
            Timber.e(e, "Error importing preferences from JSON")
            false
        }
    }

    /**
     * Helper to write values back to DataStore, respecting their original types.
     */
    private fun assignPreferenceValue(prefs: MutablePreferences, keyName: String, value: Any?) {
        when (value) {
            is String -> {
                // If the value is a String, it might be a plain string OR a JSON representation of a complex object.
                // The `stringPreferencesKey` handles both cases correctly.
                prefs[stringPreferencesKey(keyName)] = value
            }
            is Boolean -> prefs[booleanPreferencesKey(keyName)] = value
            is Double -> { // Gson deserializes all numbers as Double.
                // If it's a whole number, decide if it was an Int or Long.
                if (value == value.toLong().toDouble()) {
                    if (value <= Int.MAX_VALUE && value >= Int.MIN_VALUE) {
                        prefs[intPreferencesKey(keyName)] = value.toInt()
                    } else {
                        prefs[longPreferencesKey(keyName)] = value.toLong()
                    }
                } else { // It's a floating-point number.
                    prefs[floatPreferencesKey(keyName)] = value.toFloat()
                }
            }
            // `assignPreferenceValue` does not need to handle lists, as Gson deserializes them into LinkedTreeMap
            // which can be converted back to a JSON string if needed, but our current save logic just needs the string.
            else -> {
                // For complex objects deserialized by Gson (like lists, which become LinkedTreeMap),
                // we re-serialize them back to a JSON string to store in DataStore.
                prefs[stringPreferencesKey(keyName)] = prettyGson.toJson(value)
            }
        }
    }
}