/**
 * test/keybar.test.ts — Unit tests for keybar module (T10).
 *
 * Tests the pure KEY_MAP data structure and verifies each key
 * is mapped to the correct byte string (ARCHITECTURE §5 / §6.3).
 *
 * Per task spec: "对纯字节表单测,DOM 部分手动验" —
 * We test the byte mappings here. DOM behavior (button creation,
 * event binding, touchstart vs click) is left to manual E2E verification
 * since jsdom is not in the project dependencies.
 */

import { describe, it, expect } from 'vitest'
import { KEY_MAP, KEYBAR_BUTTONS } from '../public/keybar.js'

describe('keybar', () => {
  describe('KEY_MAP — pure byte mappings', () => {
    it('exports KEY_MAP as a constant object', () => {
      expect(KEY_MAP).toBeDefined()
      expect(typeof KEY_MAP).toBe('object')
    })

    it('Esc maps to \\x1b (0x1B)', () => {
      expect(KEY_MAP.esc).toBe('\x1b')
      expect(KEY_MAP.esc.charCodeAt(0)).toBe(0x1b)
    })

    it('Shift+Tab maps to \\x1b[Z', () => {
      expect(KEY_MAP.shiftTab).toBe('\x1b[Z')
      expect(KEY_MAP.shiftTab).toHaveLength(3)
      expect(KEY_MAP.shiftTab.charCodeAt(0)).toBe(0x1b)
      expect(KEY_MAP.shiftTab.charCodeAt(1)).toBe('['.charCodeAt(0))
      expect(KEY_MAP.shiftTab.charCodeAt(2)).toBe('Z'.charCodeAt(0))
    })

    it('arrow up maps to \\x1b[A', () => {
      expect(KEY_MAP.arrowUp).toBe('\x1b[A')
      expect(KEY_MAP.arrowUp).toHaveLength(3)
    })

    it('arrow down maps to \\x1b[B', () => {
      expect(KEY_MAP.arrowDown).toBe('\x1b[B')
      expect(KEY_MAP.arrowDown).toHaveLength(3)
    })

    it('arrow left maps to \\x1b[D', () => {
      expect(KEY_MAP.arrowLeft).toBe('\x1b[D')
      expect(KEY_MAP.arrowLeft).toHaveLength(3)
    })

    it('arrow right maps to \\x1b[C', () => {
      expect(KEY_MAP.arrowRight).toBe('\x1b[C')
      expect(KEY_MAP.arrowRight).toHaveLength(3)
    })

    it('Enter maps to \\r (0x0D carriage return, not \\n)', () => {
      expect(KEY_MAP.enter).toBe('\r')
      expect(KEY_MAP.enter).toHaveLength(1)
      expect(KEY_MAP.enter.charCodeAt(0)).toBe(0x0d)
      expect(KEY_MAP.enter).not.toBe('\n')
      expect(KEY_MAP.enter.charCodeAt(0)).not.toBe(0x0a)
    })

    it('Ctrl+C maps to \\x03 (0x03 ETX)', () => {
      expect(KEY_MAP.ctrlC).toBe('\x03')
      expect(KEY_MAP.ctrlC).toHaveLength(1)
      expect(KEY_MAP.ctrlC.charCodeAt(0)).toBe(0x03)
    })

    it('Tab maps to \\t (0x09)', () => {
      expect(KEY_MAP.tab).toBe('\t')
      expect(KEY_MAP.tab).toHaveLength(1)
      expect(KEY_MAP.tab.charCodeAt(0)).toBe(0x09)
    })

    it('slash maps to / (Claude Code slash-command launcher)', () => {
      expect(KEY_MAP.slash).toBe('/')
      expect(KEY_MAP.slash).toHaveLength(1)
    })

    it('Claude Code control keys map to the right bytes', () => {
      expect(KEY_MAP.escEsc).toBe('\x1b\x1b') // rewind / clear draft
      expect(KEY_MAP.ctrlR).toBe('\x12') // reverse history search
      expect(KEY_MAP.ctrlO).toBe('\x0f') // expand transcript
      expect(KEY_MAP.ctrlL).toBe('\x0c') // redraw screen
      expect(KEY_MAP.ctrlT).toBe('\x14') // task list
      expect(KEY_MAP.ctrlB).toBe('\x02') // background
      expect(KEY_MAP.ctrlD).toBe('\x04') // exit (EOF)
    })

    it('all 17 keys are defined', () => {
      const keys = Object.keys(KEY_MAP)
      expect(keys).toEqual([
        'esc',
        'escEsc',
        'shiftTab',
        'arrowUp',
        'arrowDown',
        'arrowLeft',
        'arrowRight',
        'enter',
        'ctrlC',
        'ctrlR',
        'ctrlO',
        'ctrlL',
        'ctrlT',
        'ctrlB',
        'ctrlD',
        'tab',
        'slash',
      ])
    })

    it('all mapped bytes are unique strings', () => {
      const bytes = Object.values(KEY_MAP)
      expect(bytes).toHaveLength(17)
      expect(new Set(bytes).size).toBe(17) // all unique
    })

    it('ANSI arrow sequences use correct format', () => {
      // All arrow keys follow \x1b[X pattern where X is A/B/C/D
      const arrowKeys = [KEY_MAP.arrowUp, KEY_MAP.arrowDown, KEY_MAP.arrowLeft, KEY_MAP.arrowRight]
      arrowKeys.forEach((seq) => {
        expect(seq).toMatch(/^\x1b\[./)
        expect(seq).toHaveLength(3)
      })
    })
  })

  describe('KEYBAR_BUTTONS — layout', () => {
    it('every button references a valid KEY_MAP key', () => {
      for (const b of KEYBAR_BUTTONS) {
        expect(KEY_MAP[b.keyName]).toBeDefined()
      }
    })

    it('Esc is first and marked primary (most-used interrupt)', () => {
      expect(KEYBAR_BUTTONS[0]?.keyName).toBe('esc')
      expect(KEYBAR_BUTTONS[0]?.primary).toBe(true)
    })

    it('exposes all Claude Code keys as buttons', () => {
      const keys = KEYBAR_BUTTONS.map((b) => b.keyName)
      expect(keys).toContain('shiftTab')
      expect(keys).toContain('slash')
      expect(keys).toContain('arrowUp')
    })
  })
})
