package chat.vojo.proxy.data

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.emptyPreferences
import java.io.IOException
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import chat.vojo.proxy.core.AppSettings
import chat.vojo.proxy.core.ProxyConfig
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

/** Everything we persist: the saved servers plus app-wide settings. */
@Serializable
data class ProxyState(
    val configs: List<ProxyConfig> = emptyList(),
    val settings: AppSettings = AppSettings(),
) {
    val activeConfig: ProxyConfig?
        get() = configs.firstOrNull { it.id == settings.activeConfigId }
            ?: configs.firstOrNull()
}

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vojo_proxy")
private val STATE_KEY = stringPreferencesKey("state")
private val json = Json { ignoreUnknownKeys = true; encodeDefaults = true }

/** Single source of truth for persisted configuration, backed by Preferences DataStore. */
class ConfigStore(context: Context) {
    private val store = context.applicationContext.dataStore

    val state: Flow<ProxyState> = store.data
        // Only swallow IO/corruption (DataStore's documented failure mode);
        // rethrow anything else so a real bug isn't hidden as "no data".
        .catch { e -> if (e is IOException) emit(emptyPreferences()) else throw e }
        .map { prefs ->
            prefs[STATE_KEY]?.let { runCatching { json.decodeFromString<ProxyState>(it) }.getOrNull() }
                ?: ProxyState()
        }

    suspend fun current(): ProxyState = state.first()

    suspend fun update(transform: (ProxyState) -> ProxyState) {
        store.edit { prefs ->
            val cur = prefs[STATE_KEY]?.let {
                runCatching { json.decodeFromString<ProxyState>(it) }.getOrNull()
            } ?: ProxyState()
            prefs[STATE_KEY] = json.encodeToString(transform(cur))
        }
    }

    suspend fun upsertConfig(config: ProxyConfig) = update { state ->
        val others = state.configs.filterNot { it.id == config.id }
        state.copy(configs = others + config)
    }

    suspend fun deleteConfig(id: String) = update { state ->
        val configs = state.configs.filterNot { it.id == id }
        val active = if (state.settings.activeConfigId == id) configs.firstOrNull()?.id else state.settings.activeConfigId
        state.copy(configs = configs, settings = state.settings.copy(activeConfigId = active))
    }

    suspend fun setActive(id: String) = update { it.copy(settings = it.settings.copy(activeConfigId = id)) }

    suspend fun updateSettings(transform: (AppSettings) -> AppSettings) =
        update { it.copy(settings = transform(it.settings)) }
}
