package com.ponzischeme89.memby.ui.detail

/**
 * Where a viewer was on a detail page, so that leaving and coming back does not undo it.
 *
 * A detail page is an overlay: closing it removes the composable entirely, which takes
 * `rememberSaveable` with it. Without somewhere outside the composition to keep this,
 * pressing Back on episode 9 of season 3 and opening the show again lands on Overview,
 * season 1, with focus on Play — every single time.
 *
 * Deliberately process-scoped rather than persisted. It is a convenience within a sitting;
 * a TV switched on the next morning should open a show where the *show* is up to, which is
 * what [defaultSeason] already decides.
 */
data class DetailPosition(
    val tabKey: String = DetailTab.OVERVIEW.key,
    /** Null means "not chosen yet" — [defaultSeason] still gets to pick. */
    val season: Int? = null,
    val zone: DetailZone = DetailZone.PLAY,
    val episodeIndex: Int = 0,
    val relatedIndex: Int = 0,
)

/**
 * Which band of the page held focus. Restoring the *tab* without the band is worse than
 * restoring neither: the viewer left with focus on an episode and comes back to a page
 * that looks identical but answers Down differently.
 */
enum class DetailZone { PLAY, TABS, CONTENT, RELATED }

/**
 * A small, capped, most-recently-used store of positions keyed by item id.
 *
 * Capped because a long browse would otherwise accumulate one entry per poster the viewer
 * pressed OK on, and none of them matter once they have scrolled out of memory.
 */
class DetailPositionStore(private val maxEntries: Int = DEFAULT_MAX_ENTRIES) {
    private val positions = LinkedHashMap<String, DetailPosition>(maxEntries, 0.75f, true)

    @Synchronized
    fun get(itemId: String): DetailPosition = positions[itemId] ?: DetailPosition()

    @Synchronized
    fun update(itemId: String, transform: (DetailPosition) -> DetailPosition) {
        if (itemId.isBlank()) return
        positions[itemId] = transform(positions[itemId] ?: DetailPosition())
        while (positions.size > maxEntries) {
            val oldest = positions.keys.firstOrNull() ?: break
            positions.remove(oldest)
        }
    }

    @Synchronized
    fun clear() = positions.clear()

    @Synchronized
    fun size(): Int = positions.size

    companion object {
        const val DEFAULT_MAX_ENTRIES = 16
    }
}

/**
 * The one store the detail pages share. A plain global rather than a `ServiceLocator`
 * entry: it holds nothing a preview, a screenshot test or a signed-out user could misuse,
 * and every reader of it is a composable that already runs without injection.
 */
val detailPositions = DetailPositionStore()
