package com.fabledsword.minstrel.home.ui

import com.fabledsword.minstrel.models.PlaylistRef
import com.fabledsword.minstrel.models.SystemPlaylistsStatus
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class BuildPlaylistsRowTest {

    private fun user(id: String, cached: Boolean) =
        PlaylistRef(id = id, userId = "u", name = id, trackCount = 1, fullyCached = cached)

    @Test
    fun `offline row leads with pools then available before greyed`() {
        val owned = listOf(user("partial", cached = false), user("full", cached = true))
        val row = buildPlaylistsRow(owned, SystemPlaylistsStatus(), offline = true)

        assertTrue(row[0] is PlaylistRowItem.OfflinePool)
        assertTrue(row[1] is PlaylistRowItem.OfflinePool)
        // Fully-cached "full" comes before the greyed "partial".
        val reals = row.filterIsInstance<PlaylistRowItem.Real>().map { it.playlist.id }
        assertEquals(listOf("full", "partial"), reals)
    }

    @Test
    fun `offline greys a refreshable system playlist even when fully cached`() {
        val forYou = PlaylistRef(
            id = "fy",
            userId = "u",
            name = "For You",
            systemVariant = "for_you",
            trackCount = 1,
            fullyCached = true,
        )
        val row = buildPlaylistsRow(
            listOf(forYou, user("u1", cached = true)),
            SystemPlaylistsStatus(),
            offline = true,
        )
        // The fully-cached user playlist is available; the refreshable system
        // mix needs the server, so it greys out and sorts after.
        val reals = row.filterIsInstance<PlaylistRowItem.Real>().map { it.playlist.id }
        assertEquals(listOf("u1", "fy"), reals)
    }

    @Test
    fun `offline row drops building-pending placeholders`() {
        val row = buildPlaylistsRow(emptyList(), SystemPlaylistsStatus(), offline = true)
        assertTrue(row.none { it is PlaylistRowItem.Placeholder })
    }

    @Test
    fun `online row has no offline pools and keeps system-slot placeholders`() {
        val row = buildPlaylistsRow(emptyList(), SystemPlaylistsStatus(), offline = false)
        assertTrue(row.none { it is PlaylistRowItem.OfflinePool })
        assertTrue(row.any { it is PlaylistRowItem.Placeholder })
    }

    private fun songsLike(id: String, cached: Boolean) = PlaylistRef(
        id = id,
        userId = "u",
        name = "Songs like $id",
        systemVariant = "songs_like_artist",
        trackCount = 25,
        fullyCached = cached,
    )

    @Test
    fun `songs-like no longer appears in the main playlists carousel`() {
        val owned = listOf(songsLike("a", cached = true), user("u1", cached = true))
        val row = buildPlaylistsRow(owned, SystemPlaylistsStatus(), offline = false)
        val reals = row.filterIsInstance<PlaylistRowItem.Real>().map { it.playlist.id }
        assertTrue("a" !in reals, "songs-like mix leaked into the Playlists row: $reals")
        assertTrue("u1" in reals)
    }

    @Test
    fun `online songs-like row shows every generated mix uncapped`() {
        // Six generated mixes — the old carousel capped at 3; the dedicated row shows all.
        val owned = (1..6).map { songsLike("a$it", cached = true) }
        val row = buildSongsLikeRow(owned, SystemPlaylistsStatus(), offline = false)
        val reals = row.filterIsInstance<PlaylistRowItem.Real>().map { it.playlist.id }
        assertEquals((1..6).map { "a$it" }, reals)
    }

    @Test
    fun `online songs-like row shows placeholders when none generated`() {
        val row = buildSongsLikeRow(emptyList(), SystemPlaylistsStatus(), offline = false)
        assertTrue(row.isNotEmpty())
        assertTrue(row.all { it is PlaylistRowItem.Placeholder })
    }

    @Test
    fun `offline songs-like row shows cached mixes available-first, none hides it`() {
        val owned = listOf(songsLike("partial", cached = false), songsLike("full", cached = true))
        val row = buildSongsLikeRow(owned, SystemPlaylistsStatus(), offline = true)
        val reals = row.filterIsInstance<PlaylistRowItem.Real>().map { it.playlist.id }
        // Fully-cached songs-like mix (not refreshable) is available; the
        // un-cached one greys and sorts after. No placeholders offline.
        assertEquals(listOf("full", "partial"), reals)
        assertTrue(row.none { it is PlaylistRowItem.Placeholder })

        assertTrue(buildSongsLikeRow(emptyList(), SystemPlaylistsStatus(), offline = true).isEmpty())
    }
}
