package com.fabledsword.minstrel.player

import android.content.Context
import coil3.SingletonImageLoader
import coil3.request.ImageRequest
import com.fabledsword.minstrel.di.ApplicationScope
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Warms Coil's memory + disk cache with the *next* queued track's
 * cover so when the player advances, the on-screen AsyncImage and
 * the NowPlaying dominant-color extraction both hit a cache instead
 * of waiting on the network.
 *
 * Pulls the next cover URL out of [PlayerController.uiState] every
 * time the queue or current-index changes, deduplicates, and fires
 * a Coil enqueue. Fire-and-forget — the request's own Disposable is
 * discarded; Coil's cache write happens on the loader's background
 * thread regardless of subscription.
 *
 * Constructed at app launch via the construct-the-singleton trick
 * in [com.fabledsword.minstrel.MinstrelApplication].
 */
@Singleton
class CoverPrefetcher @Inject constructor(
    @ApplicationContext private val context: Context,
    private val playerController: PlayerController,
    @ApplicationScope private val scope: CoroutineScope,
) {
    init {
        scope.launch {
            playerController.uiState
                .mapNotNull { it.peekNextCoverUrl() }
                .filter { it.isNotEmpty() }
                .distinctUntilChanged()
                .collect(::prefetch)
        }
    }

    private fun prefetch(url: String) {
        val request = ImageRequest.Builder(context)
            .data(url)
            .build()
        SingletonImageLoader.get(context).enqueue(request)
    }
}

/**
 * Cover URL for `queueIndex + 1`, or null when the player is at the
 * end of the queue / has no queue. Empty string is preserved so the
 * caller can filter it; we don't want to prefetch a placeholder URL.
 */
private fun PlayerUiState.peekNextCoverUrl(): String? =
    queue.getOrNull(queueIndex + 1)?.coverUrl
