package com.ponzischeme89.memby.ui.player

import com.ponzischeme89.memby.data.DEFAULT_SEEK_INTERVAL_SECONDS
import com.ponzischeme89.memby.data.normalizeSeekIntervalSeconds
import org.junit.Assert.assertEquals
import org.junit.Test

class SeekControlsTest {

    private val duration = 90L * 60_000L

    @Test
    fun `a press moves one interval from the playhead`() {
        val preview = accumulateSeek(
            previous = null,
            positionMs = 60_000L,
            durationMs = duration,
            stepMs = 30_000L,
            forward = true,
        )
        assertEquals(90_000L, preview.targetMs)
        assertEquals(30_000L, preview.offsetMs)
    }

    /**
     * The reason presses accumulate against the preview rather than the playhead: the film
     * is still running while somebody works down the remote, and measuring each step from
     * the live position would quietly swallow part of every press after the first.
     */
    @Test
    fun `repeated presses accumulate against the preview, not the moving playhead`() {
        var preview = accumulateSeek(null, 60_000L, duration, 30_000L, forward = true)
        preview = accumulateSeek(preview, 61_200L, duration, 30_000L, forward = true)
        preview = accumulateSeek(preview, 62_400L, duration, 30_000L, forward = true)

        assertEquals(150_000L, preview.targetMs)
        assertEquals(90_000L, preview.offsetMs)
        assertEquals("1 min 30 secs", seekAmountLabel(preview.offsetMs))
    }

    /** Reversing mid-burst is how a viewer corrects an overshoot without leaving the OSD. */
    @Test
    fun `a reversal reduces the running total`() {
        var preview = accumulateSeek(null, 60_000L, duration, 10_000L, forward = true)
        preview = accumulateSeek(preview, 60_500L, duration, 10_000L, forward = true)
        preview = accumulateSeek(preview, 61_000L, duration, 10_000L, forward = false)

        assertEquals(70_000L, preview.targetMs)
        assertEquals(10_000L, preview.offsetMs)
    }

    @Test
    fun `rewinding past the start stops at the start`() {
        val preview = accumulateSeek(null, 8_000L, duration, 30_000L, forward = false)
        assertEquals(0L, preview.targetMs)
        assertEquals(-8_000L, preview.offsetMs)
    }

    /**
     * Landing on the final moment ends playback, which on an episode rolls straight into
     * the next one — a skip forward must never be a way to finish what you are watching.
     */
    @Test
    fun `fast-forwarding stops short of the end`() {
        val preview = accumulateSeek(null, duration - 5_000L, duration, 30_000L, forward = true)
        assertEquals(duration - 1_000L, preview.targetMs)
    }

    @Test
    fun `amounts read as words rather than signed numbers`() {
        assertEquals("10 seconds", seekAmountLabel(10_000L))
        assertEquals("30 seconds", seekAmountLabel(-30_000L))
        assertEquals("1 min", seekAmountLabel(60_000L))
        assertEquals("2 mins", seekAmountLabel(-120_000L))
        assertEquals("1 min 30 secs", seekAmountLabel(90_000L))
        assertEquals("2 mins 10 secs", seekAmountLabel(130_000L))
    }

    /**
     * Both halves of the position line are formatted against the *duration*, so the digits
     * do not change shape as somebody skips past the hour of a two-hour film.
     */
    @Test
    fun `the position line matches the shape of the total`() {
        assertEquals("12:34 / 42:00", seekPositionLabel(754_000L, 2_520_000L))
        assertEquals("0:12:34 / 1:45:00", seekPositionLabel(754_000L, 6_300_000L))
        assertEquals("1:00:00 / 1:45:00", seekPositionLabel(3_600_000L, 6_300_000L))
    }

    /** An interval this build has no vocabulary for is the default, never an odd skip. */
    @Test
    fun `only the offered intervals survive`() {
        assertEquals(10, normalizeSeekIntervalSeconds(10))
        assertEquals(20, normalizeSeekIntervalSeconds(20))
        assertEquals(30, normalizeSeekIntervalSeconds(30))
        assertEquals(DEFAULT_SEEK_INTERVAL_SECONDS, normalizeSeekIntervalSeconds(0))
        assertEquals(DEFAULT_SEEK_INTERVAL_SECONDS, normalizeSeekIntervalSeconds(45))
        assertEquals(DEFAULT_SEEK_INTERVAL_SECONDS, normalizeSeekIntervalSeconds(-10))
    }
}
