// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
// SPDX-License-Identifier: GPL-3.0-or-later

package space.rcmd.android.sizzle.input

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

/**
 * Verifies the shared hold-to-repeat used by all three hardware inputs
 * ([InputRouter.pressRepeat] / [releaseRepeat]) on a virtual clock: a repeatable
 * nav action fires once on press then accelerates while held and stops the instant
 * it's released; a one-shot action fires exactly once no matter how long it's held.
 */
@OptIn(ExperimentalCoroutinesApi::class)
class InputRouterTest {

    @Test
    fun repeatableNavRepeatsWhileHeldThenStopsOnRelease() = runTest {
        val router = InputRouter(repeatDispatcher = StandardTestDispatcher(testScheduler))
        val got = mutableListOf<InputAction>()
        val collector = launch { router.actions.collect { got.add(it) } }
        runCurrent() // let the collector subscribe before we emit

        router.pressRepeat("dpadUp", InputAction.NavUp)
        runCurrent()
        assertEquals("press fires exactly once immediately", 1, got.size)

        // Hold for ~1s: 320ms to the first repeat, then accelerating (×0.8) steps.
        advanceTimeBy(1_000)
        runCurrent()
        val whileHeld = got.size
        assertTrue("should repeat several times while held (was $whileHeld)", whileHeld >= 4)
        assertTrue("every emission is the held action", got.all { it == InputAction.NavUp })

        // Release: no further emissions, ever.
        router.releaseRepeat("dpadUp")
        advanceTimeBy(5_000)
        runCurrent()
        assertEquals("no repeats after release", whileHeld, got.size)

        collector.cancel()
    }

    @Test
    fun oneShotActionDoesNotRepeat() = runTest {
        val router = InputRouter(repeatDispatcher = StandardTestDispatcher(testScheduler))
        val got = mutableListOf<InputAction>()
        val collector = launch { router.actions.collect { got.add(it) } }
        runCurrent()

        router.pressRepeat("btnA", InputAction.PlayPause)
        advanceTimeBy(5_000)
        runCurrent()

        assertEquals("a non-repeatable action fires once even when held", 1, got.size)
        assertEquals(InputAction.PlayPause, got.single())
        collector.cancel()
    }

    @Test
    fun releasingAModifierKeyDoesNotStopTheHeldNavRepeat() = runTest {
        val router = InputRouter(repeatDispatcher = StandardTestDispatcher(testScheduler))
        val got = mutableListOf<InputAction>()
        val collector = launch { router.actions.collect { got.add(it) } }
        runCurrent()

        router.pressRepeat("dpadDown", InputAction.NavDown)
        advanceTimeBy(1_000); runCurrent()
        val beforeOtherRelease = got.size

        // Releasing some OTHER control must not stop the active repeat.
        router.releaseRepeat("someOtherButton")
        advanceTimeBy(1_000); runCurrent()
        assertTrue("repeat continues after an unrelated release", got.size > beforeOtherRelease)

        router.releaseRepeat("dpadDown")
        val afterRelease = got.size
        advanceTimeBy(2_000); runCurrent()
        assertEquals("stops only when the held key is released", afterRelease, got.size)

        collector.cancel()
    }
}
