package wang.yaojia.webterm.terminalview

import android.view.KeyEvent
import com.termux.terminal.KeyHandler
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

/**
 * DECCKM (plan §6.3): with `cursorKeysApplication` set (`ESC[?1h`), arrow keys must emit `ESC O A`
 * (SS3) not `ESC [ A` (CSI) — hardcoding CSI would break vim/htop. We defer to Termux's [KeyHandler]
 * and drive it from the emulator's live mode bit. `KeyHandler.getCode` runs headless because its
 * `KEYCODE_*` cases are inlined compile-time constants (no runtime `android.view.KeyEvent` link).
 */
@OptIn(ExperimentalCoroutinesApi::class)
class DecckmKeyTest {

    private val noModifiers = 0

    @Test
    fun `KeyHandler emits SS3 for arrows under cursor-keys-application, CSI otherwise`() {
        // Normal (DECCKM off) → CSI.
        assertEquals("\u001b[A", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_UP, noModifiers, false, false))
        assertEquals("\u001b[B", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_DOWN, noModifiers, false, false))
        assertEquals("\u001b[C", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_RIGHT, noModifiers, false, false))
        assertEquals("\u001b[D", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_LEFT, noModifiers, false, false))

        // Application-cursor-keys (DECCKM on) → SS3.
        assertEquals("\u001bOA", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_UP, noModifiers, true, false))
        assertEquals("\u001bOB", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_DOWN, noModifiers, true, false))
        assertEquals("\u001bOC", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_RIGHT, noModifiers, true, false))
        assertEquals("\u001bOD", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_LEFT, noModifiers, true, false))
    }

    @Test
    fun `session keyBytes tracks the emulator's live DECCKM mode`() = runTest {
        val dispatcher = StandardTestDispatcher(testScheduler)
        val session = RemoteTerminalSession(
            engineSend = {},
            appendDispatcher = dispatcher,
            mainDispatcher = dispatcher,
        )
        session.bind {}

        // Before any mode change: CSI.
        assertFalse(session.emulator.isCursorKeysApplicationMode)
        assertEquals("\u001b[A", session.keyBytes(KeyEvent.KEYCODE_DPAD_UP, noModifiers))

        // ESC[?1h sets DECCKM (application cursor keys) — vim/htop turn this on.
        session.feedRemote("\u001b[?1h".toByteArray(Charsets.UTF_8))
        advanceUntilIdle()

        assertTrue(session.emulator.isCursorKeysApplicationMode, "ESC[?1h must set DECCKM")
        assertEquals("\u001bOA", session.keyBytes(KeyEvent.KEYCODE_DPAD_UP, noModifiers))

        session.close()
        advanceUntilIdle()
    }
}
