package com.ponzischeme89.memby.ui

import com.ponzischeme89.memby.data.model.BaseItem
import com.ponzischeme89.memby.data.model.UserItemData
import com.ponzischeme89.memby.ui.detail.SeasonProgress
import com.ponzischeme89.memby.ui.detail.episodeAfter
import com.ponzischeme89.memby.ui.detail.episodeEyebrow
import com.ponzischeme89.memby.ui.detail.seasonMarkers
import com.ponzischeme89.memby.ui.detail.seasonProgressLabel
import com.ponzischeme89.memby.ui.detail.seriesProgressLabel
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test

/**
 * What an episode's own detail page says, in plain JUnit.
 *
 * The season scroller's whole job is to be right about where the household is up to, so the
 * marking rules are pinned here rather than left to a screenshot to notice.
 */
class EpisodeDetailTest {

    @Test
    fun `eyebrow spells the season and episode out`() {
        assertEquals("SEASON 3 · EPISODE 4", episodeEyebrow(episode(3, 4)))
    }

    @Test
    fun `eyebrow names specials rather than season zero`() {
        assertEquals("SPECIALS · EPISODE 1", episodeEyebrow(episode(0, 1)))
    }

    @Test
    fun `an unnumbered episode has no eyebrow to show`() {
        assertNull(episodeEyebrow(BaseItem(id = "x", name = "Pilot", type = "Episode")))
    }

    @Test
    fun `seasons behind the one being watched are marked watched`() {
        val markers = seasonMarkers(library, currentSeason = 3)
        assertEquals(
            listOf(SeasonProgress.WATCHED, SeasonProgress.WATCHED, SeasonProgress.CURRENT, SeasonProgress.UPCOMING),
            markers.map { it.progress },
        )
    }

    /** Skipping an episode of season one does not put the viewer back in season one. */
    @Test
    fun `an earlier season with unwatched episodes is still behind the viewer`() {
        val markers = seasonMarkers(library, currentSeason = 3)
        val secondSeason = markers.single { it.season == 2 }
        assertEquals(SeasonProgress.WATCHED, secondSeason.progress)
        assertEquals(1, secondSeason.watchedEpisodes)
        assertEquals(2, secondSeason.totalEpisodes)
    }

    /** A season ahead that has been sampled must not read as finished. */
    @Test
    fun `later seasons stay upcoming`() {
        val markers = seasonMarkers(library, currentSeason = 1)
        assertEquals(SeasonProgress.UPCOMING, markers.single { it.season == 4 }.progress)
    }

    @Test
    fun `a fully watched season is marked watched without a current season`() {
        val markers = seasonMarkers(library, currentSeason = null)
        assertEquals(SeasonProgress.WATCHED, markers.single { it.season == 1 }.progress)
        assertEquals(SeasonProgress.UPCOMING, markers.single { it.season == 2 }.progress)
    }

    @Test
    fun `season progress reads as a count`() {
        val markers = seasonMarkers(library, currentSeason = 3)
        assertEquals("All 2 watched", seasonProgressLabel(markers.single { it.season == 1 }))
        assertEquals("1 of 2 watched", seasonProgressLabel(markers.single { it.season == 2 }))
        assertEquals("2 episodes", seasonProgressLabel(markers.single { it.season == 4 }))
    }

    @Test
    fun `the series line places the season and counts what is left`() {
        val markers = seasonMarkers(library, currentSeason = 3)
        assertEquals("Season 3 of 4  •  4 episodes left", seriesProgressLabel(markers, 3))
    }

    @Test
    fun `a finished show reports nothing left`() {
        val watched = library.map { it.copy(userData = UserItemData(played = true)) }
        val markers = seasonMarkers(watched, currentSeason = 4)
        assertEquals("Season 4 of 4", seriesProgressLabel(markers, 4))
    }

    /** A special nobody has watched must not be ticked just for sorting first. */
    @Test
    fun `specials are not watched merely by being before the current season`() {
        val withSpecials = library + episode(0, 1)
        val markers = seasonMarkers(withSpecials, currentSeason = 3)
        assertEquals(SeasonProgress.UPCOMING, markers.single { it.season == 0 }.progress)
        assertEquals("1 episode", seasonProgressLabel(markers.single { it.season == 0 }))
    }

    @Test
    fun `a watched special is still marked watched`() {
        val markers = seasonMarkers(library + episode(0, 1, played = true), currentSeason = 3)
        assertEquals(SeasonProgress.WATCHED, markers.single { it.season == 0 }.progress)
        assertEquals("Watched", seasonProgressLabel(markers.single { it.season == 0 }))
    }

    /** Specials are not a season, and counting them displaces every numbered one. */
    @Test
    fun `the series line counts numbered seasons only`() {
        val markers = seasonMarkers(library + episode(0, 1), currentSeason = 3)
        assertEquals("Season 3 of 4  •  5 episodes left", seriesProgressLabel(markers, 3))
    }

    @Test
    fun `no seasons means no progress line`() {
        assertNull(seriesProgressLabel(emptyList(), 1))
    }

    /** Position decides what is next, so a season boundary is not a stopping point. */
    @Test
    fun `the next episode crosses into the following season`() {
        val last = library.single { it.parentIndexNumber == 1 && it.indexNumber == 2 }
        assertEquals("s2e1", episodeAfter(library, last)?.id)
    }

    @Test
    fun `the last episode of the show has nothing after it`() {
        val last = library.single { it.parentIndexNumber == 4 && it.indexNumber == 2 }
        assertNull(episodeAfter(library, last))
    }

    @Test
    fun `an episode the library does not hold resolves to nothing`() {
        assertNull(episodeAfter(library, episode(9, 9)))
    }

    private val library = listOf(
        episode(1, 1, played = true),
        episode(1, 2, played = true),
        episode(2, 1, played = true),
        episode(2, 2),
        episode(3, 1, played = true),
        episode(3, 2),
        episode(4, 1),
        episode(4, 2),
    )

    private fun episode(season: Int, number: Int, played: Boolean = false) = BaseItem(
        id = "s${season}e$number",
        name = "Episode $number",
        type = "Episode",
        seriesId = "series-1",
        seriesName = "Signal Hill",
        parentIndexNumber = season,
        indexNumber = number,
        userData = UserItemData(played = played),
    )
}
