package ca.ksamad.encore.ui

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import ca.ksamad.encore.EncoreApplication
import ca.ksamad.encore.data.AlbumViewMode
import ca.ksamad.encore.data.ConnectionSettings
import ca.ksamad.encore.mpd.MpdConnectionState
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn

/** Which top-level screen to show. Derived from connection + persisted settings. */
sealed interface AppScreen {
    /** Reading settings / auto-connecting — a brief splash, never the connect form. */
    data object Loading : AppScreen

    /** First run, after a disconnect, or a failed connection. */
    data class Connect(
        val settings: ConnectionSettings,
        val error: String?,
    ) : AppScreen

    /** Connected: show playback. */
    data object Player : AppScreen
}

/**
 * Thin UI-facing layer over the app-scoped [ca.ksamad.encore.playback.MpdConnectionManager]: it
 * exposes the manager's flows to Compose and derives the top-level [AppScreen]. The connection
 * itself lives in the manager (shared with the foreground service), so it survives this ViewModel
 * being cleared on Activity recreation.
 */
class PlayerViewModel(application: Application) : AndroidViewModel(application) {
    private val manager = (application as EncoreApplication).manager

    val status = manager.status
    val currentSong = manager.currentSong
    val serverHost = manager.serverHost
    val settings = manager.settings
    val albumViewMode = manager.albumViewMode
    val volumeStep = manager.volumeStep
    val gridColumns = manager.gridColumns

    val screen: StateFlow<AppScreen> =
        combine(
                manager.connectionState,
                manager.bootstrapping,
                manager.settings,
            ) { connection, booting, saved ->
                when {
                    booting -> AppScreen.Loading
                    connection is MpdConnectionState.Connected -> AppScreen.Player
                    connection is MpdConnectionState.Connecting -> AppScreen.Loading
                    connection is MpdConnectionState.Error ->
                        AppScreen.Connect(saved, connection.message)
                    else -> AppScreen.Connect(saved, null) // Disconnected after startup
                }
            }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = AppScreen.Loading,
            )

    fun connect(
        host: String,
        port: Int,
        password: String? = null,
    ) = manager.connect(host, port, password)

    fun disconnect() = manager.disconnect()

    fun resetSettings() = manager.resetSettings()

    fun togglePlayPause() = manager.togglePlayPause()

    fun next() = manager.next()

    fun previous() = manager.previous()

    fun seekTo(seconds: Double) = manager.seekTo(seconds)

    fun setVolume(volume: Int) = manager.setVolume(volume)

    fun setRepeat(on: Boolean) = manager.setRepeat(on)

    fun setRandom(on: Boolean) = manager.setRandom(on)

    fun setConsume(on: Boolean) = manager.setConsume(on)

    fun setAlbumViewMode(mode: AlbumViewMode) = manager.setAlbumViewMode(mode)

    fun setVolumeStep(step: Int) = manager.setVolumeStep(step)

    fun setGridColumns(columns: Int) = manager.setGridColumns(columns)

    fun playAlbum(
        album: String,
        albumArtist: String?,
    ) = manager.playAlbum(album, albumArtist)

    fun queueAlbum(
        album: String,
        albumArtist: String?,
    ) = manager.queueAlbum(album, albumArtist)

    fun playAlbumNext(
        album: String,
        albumArtist: String?,
    ) = manager.playAlbumNext(album, albumArtist)

    fun playTracks(uris: List<String>) = manager.playTracks(uris)

    fun queueTrack(uri: String) = manager.queueTrack(uri)

    fun playTrackNext(uri: String) = manager.playTrackNext(uri)

    suspend fun loadAlbumTracks(
        album: String,
        albumArtist: String?,
    ) = manager.loadAlbumTracks(album, albumArtist)

    suspend fun loadAlbums() = manager.loadAlbums()

    fun updateDatabase() = manager.updateDatabase()

    fun rescanDatabase() = manager.rescanDatabase()

    fun refreshArtwork() = manager.refreshArtwork()

    fun playQueueItem(songId: Int) = manager.playQueueItem(songId)

    fun removeQueueItem(songId: Int) = manager.removeQueueItem(songId)

    fun addTrackAt(
        uri: String,
        position: Int,
    ) = manager.addTrackAt(uri, position)

    fun clearQueue() = manager.clearQueue()

    suspend fun loadStatistics() = manager.loadStatistics()

    suspend fun loadQueue() = manager.loadQueue()
}
