package com.fabledsword.minstrel.playlists.data

import com.fabledsword.minstrel.models.PlaylistRef
import com.fabledsword.minstrel.player.PlayerController
import com.fabledsword.minstrel.api.ErrorCopy
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.withTimeout

private const val PLAYLIST_FETCH_TIMEOUT_MS = 8_000L

/**
 * Fetch a playlist and hand it to the player as a shuffled queue.
 *
 * Behavior matches the tile play-button contract used on Home and the
 * Playlists list: refreshable system playlists go through systemShuffle
 * (server-side rotation-aware order; tagging with the variant advances
 * rotation), everything else uses refreshDetail. System playlists are
 * then client-side shuffled so the tile feels random rather than
 * "start at the rotation head". User playlists keep their authored
 * order.
 *
 * Errors and empty mixes surface through [onMessage] for the caller to
 * present as a snackbar / toast / etc. Returns when the player has
 * accepted the queue (or an error path bailed).
 */
suspend fun playPlaylistShuffled(
    playlist: PlaylistRef,
    repository: PlaylistsRepository,
    player: PlayerController,
    onMessage: (String) -> Unit,
) {
    val detail = fetchPlaylistDetail(playlist, repository, onMessage) ?: return
    val tracks = detail.tracks.toPlayableTrackRefs()
    if (tracks.isEmpty()) {
        onMessage("Mix isn't ready yet - try again in a moment")
        return
    }
    // Drift #564: bare systemVariant string (not "playlist:<variant>") --
    // server's rotation matcher keys on the bare variant.
    val source = if (playlist.refreshable) playlist.systemVariant else null
    // System playlist tile play button == "pick a random song + shuffle
    // the rest" UX. Server's rotation-aware order still drives rotation
    // bookkeeping via `source`; the client shuffle just removes the
    // deterministic "start at rotation head" feel.
    val ordered = if (playlist.isSystem) tracks.shuffled() else tracks
    player.setQueue(ordered, initialIndex = 0, source = source)
}

private suspend fun fetchPlaylistDetail(
    playlist: PlaylistRef,
    repository: PlaylistsRepository,
    onMessage: (String) -> Unit,
): PlaylistDetailRef? = try {
    withTimeout(PLAYLIST_FETCH_TIMEOUT_MS) {
        if (playlist.refreshable && playlist.systemVariant != null) {
            repository.systemShuffle(playlist.systemVariant)
        } else {
            repository.refreshDetail(playlist.id)
        }
    }
} catch (
    @Suppress("SwallowedException") _: TimeoutCancellationException,
) {
    onMessage("Couldn't load playlist - check your connection")
    null
} catch (
    @Suppress("TooGenericExceptionCaught") e: Throwable,
) {
    onMessage("Couldn't load playlist: ${ErrorCopy.fromThrowable(e)}")
    null
}
