package com.fabledsword.minstrel.settings.ui

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.fabledsword.minstrel.auth.AuthStore
import com.fabledsword.minstrel.cache.audiocache.CacheSettings
import com.fabledsword.minstrel.cache.sync.SyncController
import com.fabledsword.minstrel.player.PlayerFactory
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

data class StorageUiState(
    val settings: CacheSettings = CacheSettings.DEFAULT,
    val usedBytes: Long = 0L,
    val isSyncing: Boolean = false,
    val isClearing: Boolean = false,
)

/**
 * Backs the Storage section of the Settings screen. Exposes the
 * persisted CacheSettings + the current SimpleCache usage, plus
 * actions for "Sync now" and "Clear cache".
 *
 * Settings changes go through AuthStore.setCacheSettings (write-
 * through to Room). The cap settings only take effect on next app
 * launch because SimpleCache is constructed once per process; the
 * UI surfaces that caveat (next commit).
 */
@HiltViewModel
class StorageViewModel @Inject constructor(
    private val authStore: AuthStore,
    private val playerFactory: PlayerFactory,
    private val syncController: SyncController,
) : ViewModel() {

    private val internal = MutableStateFlow(StorageUiState())
    val state: StateFlow<StorageUiState> = internal.asStateFlow()

    init {
        viewModelScope.launch {
            authStore.cacheSettings.collect { s ->
                internal.update { it.copy(settings = s) }
            }
        }
        refreshUsage()
    }

    fun setLikedCapBytes(bytes: Long) {
        val current = authStore.cacheSettings.value
        authStore.setCacheSettings(current.copy(likedCapBytes = bytes))
    }

    fun setRollingCapBytes(bytes: Long) {
        val current = authStore.cacheSettings.value
        authStore.setCacheSettings(current.copy(rollingCapBytes = bytes))
    }

    fun setPrefetchWindow(n: Int) {
        val clamped = n.coerceIn(1, MAX_PREFETCH_WINDOW)
        val current = authStore.cacheSettings.value
        authStore.setCacheSettings(current.copy(prefetchWindow = clamped))
    }

    fun setCacheLikedTracks(on: Boolean) {
        val current = authStore.cacheSettings.value
        authStore.setCacheSettings(current.copy(cacheLikedTracks = on))
    }

    fun syncNow() {
        if (internal.value.isSyncing) return
        viewModelScope.launch {
            internal.update { it.copy(isSyncing = true) }
            try {
                syncController.syncSafe()
            } finally {
                internal.update { it.copy(isSyncing = false) }
            }
        }
    }

    fun clearCache() {
        if (internal.value.isClearing) return
        viewModelScope.launch {
            internal.update { it.copy(isClearing = true) }
            try {
                withContext(Dispatchers.IO) {
                    // SimpleCache.getKeys() + removeResource is the safe
                    // path — releasing the cache mid-flight would crash
                    // any in-progress playback. Iterating + removing
                    // works while the cache is in use.
                    val cache = playerFactory.simpleCache
                    val keys = cache.keys.toList()
                    for (k in keys) {
                        runCatching { cache.removeResource(k) }
                    }
                }
                refreshUsage()
            } finally {
                internal.update { it.copy(isClearing = false) }
            }
        }
    }

    /** Recomputes used bytes from SimpleCache. Cheap; just reads a long. */
    private fun refreshUsage() {
        viewModelScope.launch {
            val used = withContext(Dispatchers.IO) {
                runCatching { playerFactory.simpleCache.cacheSpace }.getOrDefault(0L)
            }
            internal.update { it.copy(usedBytes = used) }
        }
    }

    companion object {
        private const val MAX_PREFETCH_WINDOW = 10
    }
}
