package wang.yaojia.webterm.session

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Test

/**
 * Guards the byte-for-byte contract with `public/keybar.ts` `KEY_MAP`.
 *
 * [WEB_KEY_MAP] is the KEY_MAP from public/keybar.ts, transcribed with the web's
 * `\xNN` escapes rebuilt from explicit hex code points (`Char(0x1B)` = ESC = web
 * `\x1b`, etc.). If keybar.ts changes a byte and KeyByteMap is not updated in
 * lockstep, [bytesMatchKeybarKeyMapExactly] fails.
 */
class KeyByteMapTest {

    private val esc = Char(0x1B).toString()

    /** webName (keybar.ts KEY_MAP property) → byte string, in keybar.ts order. */
    private val WEB_KEY_MAP: Map<String, String> = linkedMapOf(
        "esc" to esc, //             '\x1b'
        "escEsc" to esc + esc, //    '\x1b\x1b'
        "shiftTab" to esc + "[Z", // '\x1b[Z'
        "arrowUp" to esc + "[A", //  '\x1b[A'
        "arrowDown" to esc + "[B", //'\x1b[B'
        "arrowLeft" to esc + "[D", //'\x1b[D'
        "arrowRight" to esc + "[C", //'\x1b[C'
        "enter" to "\r", //          '\r'
        "ctrlC" to Char(0x03).toString(), // '\x03'
        "ctrlR" to Char(0x12).toString(), // '\x12'
        "ctrlO" to Char(0x0F).toString(), // '\x0f'
        "ctrlL" to Char(0x0C).toString(), // '\x0c'
        "ctrlT" to Char(0x14).toString(), // '\x14'
        "ctrlB" to Char(0x02).toString(), // '\x02'
        "ctrlD" to Char(0x04).toString(), // '\x04'
        "tab" to "\t", //            '\t'
        "slash" to "/", //           '/'
    )

    @Test
    fun bytesMatchKeybarKeyMapExactly() {
        for (key in KeyByteMap.Key.entries) {
            val expected = WEB_KEY_MAP[key.webName]
                ?: error("no keybar.ts KEY_MAP entry for webName='${key.webName}'")
            assertEquals(expected, KeyByteMap.bytes(key), "byte mismatch for ${key.name}")
        }
    }

    @Test
    fun everyKeybarKeyIsCoveredAndNothingExtra() {
        assertEquals(WEB_KEY_MAP.keys, KeyByteMap.Key.entries.map { it.webName }.toSet())
        assertEquals(WEB_KEY_MAP.size, KeyByteMap.Key.entries.size)
    }

    @Test
    fun declarationOrderMirrorsTheWebTable() {
        assertEquals(WEB_KEY_MAP.keys.toList(), KeyByteMap.Key.entries.map { it.webName })
    }

    @Test
    fun enterIsCarriageReturnNotLinefeed() {
        assertEquals("\r", KeyByteMap.bytes(KeyByteMap.Key.ENTER))
        assertNotEquals("\n", KeyByteMap.bytes(KeyByteMap.Key.ENTER))
    }

    @Test
    fun escIsASingleByte() {
        assertEquals(1, KeyByteMap.bytes(KeyByteMap.Key.ESC).length)
        assertEquals(0x1B, KeyByteMap.bytes(KeyByteMap.Key.ESC)[0].code)
    }
}
