package com.fabledsword.minstrel.shared.widgets.trackactions

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.fabledsword.minstrel.api.ErrorCopy
import com.fabledsword.minstrel.events.EventsStream
import com.fabledsword.minstrel.likes.data.LikesRepository
import com.fabledsword.minstrel.models.TrackRef
import com.fabledsword.minstrel.player.PlayerController
import com.fabledsword.minstrel.playlists.data.PlaylistsRepository
import com.fabledsword.minstrel.quarantine.data.QuarantineRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject

/**
 * Backs [TrackActionsSheet] with state observations + action callbacks.
 * Scoped to the call-site screen via `hiltViewModel()`. The sheet hands
 * a [TrackRef] into the action methods; the VM doesn't carry per-sheet
 * trackId state — it exposes the flows + actions the sheet needs.
 *
 * Snackbar copy emits on [transientMessages] — call sites collect this
 * and route to their existing SnackbarHostState.
 *
 * Hidden-state observation: [QuarantineRepository] doesn't yet expose
 * a Flow for the caller's quarantine list (no Room cache; see the
 * repository's class comment), so this VM holds its own snapshot of
 * the hidden track-id set. Refreshed on init, after every flag /
 * unflag action, AND on any `quarantine.*` SSE event so a hide/unhide
 * from another device updates the sheet's Hide/Unhide label without
 * a manual reload.
 */
@HiltViewModel
class TrackActionsViewModel @Inject constructor(
    private val likes: LikesRepository,
    private val quarantine: QuarantineRepository,
    private val player: PlayerController,
    private val playlists: PlaylistsRepository,
    private val eventsStream: EventsStream,
) : ViewModel() {

    private val messages = MutableSharedFlow<String>(extraBufferCapacity = 4)
    val transientMessages: SharedFlow<String> = messages.asSharedFlow()

    private val hiddenTrackIdsInternal = MutableStateFlow<Set<String>>(emptySet())
    val hiddenTrackIds: StateFlow<Set<String>> = hiddenTrackIdsInternal.asStateFlow()

    init {
        refreshHidden()
        viewModelScope.launch {
            eventsStream.events
                .filter { it.kind.startsWith("quarantine.") }
                .collect { refreshHidden() }
        }
    }

    fun isLikedFlow(trackId: String): Flow<Boolean> =
        likes.observeIsLiked(LikesRepository.ENTITY_TRACK, trackId)

    fun isHiddenFlow(trackId: String): Flow<Boolean> =
        hiddenTrackIds.map { trackId in it }

    fun playNext(track: TrackRef) {
        player.playNext(track)
        messages.tryEmit("Added to queue (next)")
    }

    fun enqueue(track: TrackRef) {
        player.enqueue(track)
        messages.tryEmit("Added to queue")
    }

    fun toggleLike(trackId: String, currentlyLiked: Boolean) {
        viewModelScope.launch {
            runCatching {
                likes.toggleLike(
                    entityType = LikesRepository.ENTITY_TRACK,
                    entityId = trackId,
                    desiredState = !currentlyLiked,
                )
            }
        }
    }

    fun startRadio(trackId: String) {
        viewModelScope.launch {
            runCatching { player.startRadio(trackId) }
                .onFailure {
                    messages.tryEmit("Couldn't start radio: ${ErrorCopy.fromThrowable(it)}")
                }
        }
    }

    fun appendToPlaylist(playlistId: String, trackId: String) {
        viewModelScope.launch {
            runCatching { playlists.appendTrack(playlistId, trackId) }
                .onSuccess { messages.tryEmit("Added to playlist") }
                .onFailure {
                    messages.tryEmit("Couldn't add to playlist: ${ErrorCopy.fromThrowable(it)}")
                }
        }
    }

    fun flag(track: TrackRef, reason: String, notes: String) {
        viewModelScope.launch {
            runCatching { quarantine.flag(track, reason, notes) }
                .onSuccess { refreshHidden() }
                .onFailure { messages.tryEmit("Couldn't hide: ${ErrorCopy.fromThrowable(it)}") }
        }
    }

    fun unflag(trackId: String) {
        viewModelScope.launch {
            runCatching { quarantine.unflag(trackId) }
                .onSuccess { refreshHidden() }
                .onFailure { messages.tryEmit("Couldn't unhide: ${ErrorCopy.fromThrowable(it)}") }
        }
    }

    private fun refreshHidden() {
        viewModelScope.launch {
            runCatching { quarantine.listMine() }
                .onSuccess { rows ->
                    hiddenTrackIdsInternal.value = rows.map { it.trackId }.toSet()
                }
            // Failure leaves the previous snapshot; the sheet still
            // shows "Hide" by default which is safe (server is
            // idempotent on re-flag).
        }
    }
}
