package com.ponzischeme89.memby.ui.player

import org.junit.Assert.assertEquals
import org.junit.Test

class PlaybackTraceTest {

    private class FakeClock(var nowMs: Long) : () -> Long {
        override fun invoke(): Long = nowMs
    }

    @Test
    fun `marks are cumulative from the moment play was pressed`() {
        val clock = FakeClock(1_000L)
        val trace = PlaybackTrace(startedAtMs = 1_000L, clock = clock)

        clock.nowMs = 1_120L
        assertEquals(120L, trace.mark(PlaybackTrace.ACTIVITY_CREATED))
        clock.nowMs = 1_400L
        assertEquals(400L, trace.mark(PlaybackTrace.STREAM_RESOLVED))
        clock.nowMs = 2_050L
        assertEquals(1_050L, trace.mark(PlaybackTrace.FIRST_FRAME))
    }

    @Test
    fun `summary reports each stage with the time it added`() {
        val clock = FakeClock(0L)
        val trace = PlaybackTrace(startedAtMs = 0L, clock = clock)

        clock.nowMs = 100L
        trace.mark(PlaybackTrace.ACTIVITY_CREATED)
        clock.nowMs = 250L
        trace.mark(PlaybackTrace.PREPARED)
        clock.nowMs = 900L
        trace.mark(PlaybackTrace.FIRST_FRAME)

        assertEquals(
            "activity=100ms(+100ms) prepared=250ms(+150ms) first_frame=900ms(+650ms)",
            trace.summary(),
        )
    }

    /**
     * A retry re-enters buffering and re-reaches READY. The viewer's wait is measured from
     * the first time they got there, not the last.
     */
    @Test
    fun `a repeated stage keeps the first time it was reached`() {
        val clock = FakeClock(0L)
        val trace = PlaybackTrace(startedAtMs = 0L, clock = clock)

        clock.nowMs = 500L
        trace.mark(PlaybackTrace.READY)
        clock.nowMs = 4_000L
        assertEquals(500L, trace.mark(PlaybackTrace.READY))
        assertEquals("ready=500ms(+500ms)", trace.summary())
    }

    /** Stages that never happened are absent, not reported as having taken no time. */
    @Test
    fun `summary omits stages that were never reached`() {
        val clock = FakeClock(0L)
        val trace = PlaybackTrace(startedAtMs = 0L, clock = clock)

        clock.nowMs = 60L
        trace.mark(PlaybackTrace.ACTIVITY_CREATED)

        assertEquals("activity=60ms(+60ms)", trace.summary())
    }

    /**
     * elapsedRealtime is monotonic, but the start instant crosses a process boundary in an
     * intent — a negative wait is meaningless and must never be printed as one.
     */
    @Test
    fun `a start instant in the future clamps to zero rather than going negative`() {
        val clock = FakeClock(500L)
        val trace = PlaybackTrace(startedAtMs = 900L, clock = clock)

        assertEquals(0L, trace.mark(PlaybackTrace.ACTIVITY_CREATED))
        assertEquals(0L, trace.elapsedMs())
    }
}
