@file:Suppress("HardCodedStringLiteral", "unused")

package eu.gaudian.translator.model.repository

import android.annotation.SuppressLint
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import eu.gaudian.translator.utils.Log
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlin.time.ExperimentalTime
import kotlin.time.Instant

object DataStoreKeys {
    val PROVIDERS_KEY = stringPreferencesKey("providers")

    val EXERCISES_KEY = stringPreferencesKey("exercises")



    val QUESTIONS_KEY = stringPreferencesKey("questions")

    // Language initialization metadata key
    val LANGUAGE_INIT_METADATA_KEY = stringPreferencesKey("language_init_metadata")

    // Language-related keys
    val SELECTED_SOURCE_LANGUAGE_KEY = stringPreferencesKey("selected_source_language")
    val SELECTED_TARGET_LANGUAGE_KEY = stringPreferencesKey("selected_target_language")

    val DEFAULT_LANGUAGES_KEY = stringPreferencesKey("default_languages")
    val CUSTOM_LANGUAGES_KEY = stringPreferencesKey("custom_language")
    val ALL_LANGUAGES_KEY = stringPreferencesKey("all_languages")
    val LANGUAGE_HISTORY_KEY = stringPreferencesKey("language_history")
    val FAVORITE_LANGUAGES_KEY = stringPreferencesKey("favorite_languages")
    val SELECTED_DICTIONARY_LANGUAGE_KEY = stringPreferencesKey("selected_dictionary_language")

    val SELECTED_TRANSLATION_MODEL_KEY = stringPreferencesKey("selected_translation_model")
    val SELECTED_EXERCISE_MODEL_KEY = stringPreferencesKey("selected_exercise_model")
    val SELECTED_VOCABULARY_MODEL_KEY = stringPreferencesKey("selected_vocabulary_model")
    val SELECTED_DICTIONARY_MODEL_KEY = stringPreferencesKey("selected_dictionary_model")

    val TRANSLATION_HISTORY_KEY = stringPreferencesKey("translation_history")
    val API_LOGS_KEY = stringPreferencesKey("api_logs")
}

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "app_data")

suspend inline fun <reified T> DataStore<Preferences>.saveObject(key: Preferences.Key<String>, obj: T) {
    edit { preferences ->
        val jsonString = Json.encodeToString(obj)
        preferences[key] = jsonString
    }
}

suspend fun DataStore<Preferences>.saveStringSet(key: Preferences.Key<Set<String>>, set: Set<String>) {
    edit { preferences ->
        preferences[key] = set
    }
}

fun DataStore<Preferences>.loadStringSet(key: Preferences.Key<Set<String>>): Flow<Set<String>> {
    return data.map { preferences ->
        preferences[key] ?: emptySet()
    }
}

inline fun <reified T> DataStore<Preferences>.loadObject(key: Preferences.Key<String>): Flow<T?> {
    return data.map { preferences ->
        val jsonString = preferences[key]
        if (jsonString != null) {
            Json.decodeFromString<T>(jsonString)
        } else {
            null
        }
    }.catch { exception ->
        if (exception is SerializationException) {
            Log.w("DataStore: Failed to decode object for key '$key', clearing it.", exception)
            this@loadObject.edit { it.remove(key) }
            emit(null)
        } else {
            throw exception
        }
    }
}

@SuppressLint("SuspiciousIndentation")
suspend inline fun <reified T> DataStore<Preferences>.saveObjectList(key: Preferences.Key<String>, list: List<T>) {
    try {
    edit { preferences ->
            try {
        val jsonString = Json.encodeToString(list)
                jsonString.also { preferences[key] = it }
            } catch (e: Exception) {
                Log.e("DataStore", "Failed to encode list to JSON", e)
                throw e
            }
        }
    } catch (e: Exception) {
        Log.e("DataStore", "Failed to save list to DataStore", e)
        throw e
    }
}


inline fun <reified T> DataStore<Preferences>.loadObjectList(key: Preferences.Key<String>): Flow<List<T>> {
    return data.map { preferences ->
        val jsonString = preferences[key]
        if (jsonString != null) {
            Json.decodeFromString<List<T>>(jsonString)
        } else {
            emptyList()
        }
    }.catch { exception ->
        if (exception is SerializationException) {
            Log.w("DataStore", "Failed to decode list for key '$key', clearing it.", exception)
            this@loadObjectList.edit { it.remove(key) } // Clears the bad data
            emit(emptyList()) // Provides a default value to continue
        } else {
            throw exception
        }
    }
}

suspend fun DataStore<Preferences>.clear(key: Preferences.Key<String>) {
    edit { preferences ->
        preferences.remove(key)
    }
}

@kotlinx.serialization.Serializable
data class LanguageInitializationMetadata(
    val appVersion: String?,
    val systemLocale: String,
    val timestamp: Long
)

@OptIn(ExperimentalTime::class)
object InstantSerializer : KSerializer<Instant> {
    override val descriptor: SerialDescriptor =
        PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: Instant) {
        encoder.encodeString(value.toString())
    }

    override fun deserialize(decoder: Decoder): Instant {
        return Instant.parse(decoder.decodeString())
    }
}