package com.keylightpiano.pianotimeline.player

import android.content.Context
import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.cache.ContentMetadata
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
import androidx.media3.datasource.cache.SimpleCache
import java.io.File

/**
 * Holds the ONE and only SimpleCache instance for the whole app.
 *
 * SimpleCache is extremely strict: you may construct exactly one instance
 * pointing at a given directory for the lifetime of the process. A second
 * instance on the same folder throws. So this MUST be a singleton.
 *
 * The cache lives in the app's cache dir (`cache/media`), so the OS can reclaim
 * it under storage pressure and "clear app data" wipes it. Capacity is capped
 * with an LRU evictor — once we exceed the limit, least-recently-used media is
 * dropped automatically.
 */
@OptIn(UnstableApi::class)
object MediaCache {

    // 512 MB cap — generous for piano recordings, which are a few MB each.
    private const val MAX_CACHE_BYTES = 512L * 1024L * 1024L

    @Volatile
    private var instance: SimpleCache? = null

    fun get(context: Context): SimpleCache {
        return instance ?: synchronized(this) {
            instance ?: createCache(context).also { instance = it }
        }
    }

    private fun createCache(context: Context): SimpleCache {
        val dir = File(context.cacheDir, "media")
        val evictor = LeastRecentlyUsedCacheEvictor(MAX_CACHE_BYTES)
        val dbProvider = StandaloneDatabaseProvider(context)
        return SimpleCache(dir, evictor, dbProvider)
    }

    /**
     * Total bytes currently cached. Useful for a "clear cache" settings screen
     * that wants to show how much is stored.
     */
    fun cacheSizeBytes(context: Context): Long = get(context).cacheSpace

    /**
     * Whether the media at [url] is FULLY cached on disk — i.e. playable with no
     * network. SimpleCache keys entries by the data-spec key, which for our
     * ProgressiveMediaSource is the URL string. `getCachedLength(key, 0, length)`
     * returns the number of contiguous cached bytes starting at position 0 (or a
     * negative "hole" length if position 0 isn't cached at all); if that equals
     * the total content length, the whole file is present.
     *
     * The total length comes from ContentMetadata, which SimpleCache populates
     * once the file has been read through at least once. If we don't yet know the
     * length (<= 0), we treat it as not-fully-cached — a safe default, since a
     * partially-streamed track shouldn't count as offline-playable.
     */
    fun isFullyCached(context: Context, url: String): Boolean {
        val cache = get(context)
        val contentLength = ContentMetadata.getContentLength(cache.getContentMetadata(url))
        if (contentLength <= 0L) return false
        val cachedFromStart = cache.getCachedLength(url, 0, contentLength)
        return cachedFromStart >= contentLength
    }

    /**
     * Clears everything from the cache. Must be called when no player is actively
     * reading — call it while playback is stopped.
     */
    fun clear(context: Context) {
        val cache = get(context)
        cache.keys.toList().forEach { key ->
            cache.removeResource(key)
        }
    }
}
