package ca.ksamad.encore.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.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

// A single process-wide DataStore instance, tied to the application context via
// this property delegate (the recommended pattern — creating more than one
// DataStore for the same file throws).
private val Context.dataStore: DataStore<Preferences> by
    preferencesDataStore(name = "connection_settings")

/**
 * Reads and writes the persisted [ConnectionSettings] using Preferences DataStore. Reads come back
 * as a [Flow] that emits on every change; the write is a `suspend` transaction.
 */
class SettingsRepository(private val context: Context) {
    private object Keys {
        val HOST = stringPreferencesKey("host")
        val PORT = intPreferencesKey("port")
        val PASSWORD = stringPreferencesKey("password")
        val ALBUM_VIEW_MODE = stringPreferencesKey("album_view_mode")
        val VOLUME_STEP = intPreferencesKey("volume_step")
        val GRID_COLUMNS = intPreferencesKey("grid_columns")
    }

    /** How the albums library is laid out; emits on every change, defaulted when unset. */
    val albumViewMode: Flow<AlbumViewMode> =
        context.dataStore.data.map { prefs -> AlbumViewMode.fromName(prefs[Keys.ALBUM_VIEW_MODE]) }

    suspend fun setAlbumViewMode(mode: AlbumViewMode) {
        context.dataStore.edit { prefs -> prefs[Keys.ALBUM_VIEW_MODE] = mode.name }
    }

    /**
     * How many percent each hardware volume-key press moves the server volume; emits on every
     * change, clamped into range and defaulted when unset.
     */
    val volumeStep: Flow<Int> =
        context.dataStore.data.map { prefs ->
            (prefs[Keys.VOLUME_STEP] ?: DEFAULT_VOLUME_STEP).coerceIn(MIN_VOLUME_STEP, MAX_VOLUME_STEP)
        }

    suspend fun setVolumeStep(step: Int) {
        context.dataStore.edit { prefs ->
            prefs[Keys.VOLUME_STEP] = step.coerceIn(MIN_VOLUME_STEP, MAX_VOLUME_STEP)
        }
    }

    /**
     * How many columns the albums grid layout uses; emits on every change, clamped into range and
     * defaulted when unset.
     */
    val gridColumns: Flow<Int> =
        context.dataStore.data.map { prefs ->
            (prefs[Keys.GRID_COLUMNS] ?: DEFAULT_GRID_COLUMNS).coerceIn(MIN_GRID_COLUMNS, MAX_GRID_COLUMNS)
        }

    suspend fun setGridColumns(columns: Int) {
        context.dataStore.edit { prefs ->
            prefs[Keys.GRID_COLUMNS] = columns.coerceIn(MIN_GRID_COLUMNS, MAX_GRID_COLUMNS)
        }
    }

    /**
     * The saved settings, or `null` if the user has never connected (no host persisted yet).
     * Callers use the null case to show first-run UI / decide whether to auto-connect.
     */
    val settingsOrNull: Flow<ConnectionSettings?> =
        context.dataStore.data.map { prefs ->
            val host = prefs[Keys.HOST] ?: return@map null
            ConnectionSettings(
                host = host,
                port = prefs[Keys.PORT] ?: ConnectionSettings.DEFAULT.port,
                password = prefs[Keys.PASSWORD],
            )
        }

    /**
     * Same as [settingsOrNull] but falling back to [ConnectionSettings.DEFAULT] for form prefill.
     */
    val settings: Flow<ConnectionSettings> = settingsOrNull.map { it ?: ConnectionSettings.DEFAULT }

    suspend fun save(settings: ConnectionSettings) {
        context.dataStore.edit { prefs ->
            prefs[Keys.HOST] = settings.host
            prefs[Keys.PORT] = settings.port
            // Drop the key entirely for a password-less server so we never persist a
            // stray empty string (and a later read comes back as null, not "").
            val pw = settings.password
            if (pw.isNullOrBlank()) prefs.remove(Keys.PASSWORD) else prefs[Keys.PASSWORD] = pw
        }
    }

    /** Forget all persisted settings, returning to first-run defaults. */
    suspend fun clear() {
        context.dataStore.edit { it.clear() }
    }

    companion object {
        /** Volume-step bounds and default (percent per hardware volume-key press). */
        const val DEFAULT_VOLUME_STEP = 1
        const val MIN_VOLUME_STEP = 1
        const val MAX_VOLUME_STEP = 10

        /** Albums-grid column-count bounds and default. */
        const val DEFAULT_GRID_COLUMNS = 2
        const val MIN_GRID_COLUMNS = 1
        const val MAX_GRID_COLUMNS = 4
    }
}
