/**
 * public/keybar.ts — Mobile/desktop touch key bar, tuned for Claude Code.
 *
 * Buttons send terminal byte sequences a phone soft keyboard can't easily
 * produce, most-used first. Each button shows a short function caption under
 * the glyph so it's self-documenting on touch (where tooltips don't appear).
 * Order/selection follows the official Claude Code keyboard-shortcut docs
 * (interactive-mode / keybindings).
 *
 *   Esc        \x1b      interrupt Claude / dismiss            (primary)
 *   Esc·Esc    \x1b\x1b  rewind / clear draft
 *   Shift+Tab  \x1b[Z    cycle plan / auto-accept mode
 *   ↑ ↓        \x1b[A/B  select menu option / history
 *   Enter      \r        confirm
 *   Ctrl+C     \x03      cancel / quit
 *   Ctrl+R     \x12      reverse-search command history
 *   Ctrl+O     \x0f      expand transcript / tool detail
 *   Ctrl+L     \x0c      redraw screen
 *   Ctrl+T     \x14      toggle task list
 *   Ctrl+B     \x02      background running task
 *   Ctrl+D     \x04      exit session (EOF)
 *   Tab        \t        complete / toggle
 *   ← →        \x1b[D/C  cursor
 *   /          /         slash-command launcher
 *   🎤        (voice)    push-to-talk mic (A2; only when SpeechRecognition supported)
 */

import { isSpeechSupported } from './voice.js'

/** Key name → byte string mapping (pure data, for testing). */
export const KEY_MAP = {
  esc: '\x1b',
  escEsc: '\x1b\x1b',
  shiftTab: '\x1b[Z',
  arrowUp: '\x1b[A',
  arrowDown: '\x1b[B',
  arrowLeft: '\x1b[D',
  arrowRight: '\x1b[C',
  enter: '\r',
  ctrlC: '\x03',
  ctrlR: '\x12',
  ctrlO: '\x0f',
  ctrlL: '\x0c',
  ctrlT: '\x14',
  ctrlB: '\x02',
  ctrlD: '\x04',
  tab: '\t',
  slash: '/',
} as const

export type KeyName = keyof typeof KEY_MAP

interface KeybarButton {
  label: string
  caption: string // short function label shown under the glyph
  keyName: KeyName
  title: string // tooltip / aria-label
  primary?: boolean // styled as prominent
}

/** Button layout: most-used Claude Code keys first. */
export const KEYBAR_BUTTONS: KeybarButton[] = [
  { label: 'Esc', caption: '中断', keyName: 'esc', title: 'Esc — interrupt Claude / dismiss', primary: true },
  { label: 'Esc²', caption: '回溯', keyName: 'escEsc', title: 'Esc Esc — rewind / clear draft' },
  { label: '⇧Tab', caption: '模式', keyName: 'shiftTab', title: 'Shift+Tab — cycle plan / auto-accept mode' },
  { label: '↑', caption: '上一个', keyName: 'arrowUp', title: 'Up — previous option / history' },
  { label: '↓', caption: '下一个', keyName: 'arrowDown', title: 'Down — next option / history' },
  { label: '⏎', caption: '确认', keyName: 'enter', title: 'Enter — confirm' },
  { label: '^C', caption: '取消', keyName: 'ctrlC', title: 'Ctrl+C — cancel / quit' },
  { label: '^R', caption: '搜历史', keyName: 'ctrlR', title: 'Ctrl+R — reverse-search command history' },
  { label: '^O', caption: '详情', keyName: 'ctrlO', title: 'Ctrl+O — expand transcript / tool detail' },
  { label: '^L', caption: '重绘', keyName: 'ctrlL', title: 'Ctrl+L — redraw screen' },
  { label: '^T', caption: '任务', keyName: 'ctrlT', title: 'Ctrl+T — toggle task list' },
  { label: '^B', caption: '后台', keyName: 'ctrlB', title: 'Ctrl+B — background running task' },
  { label: '^D', caption: '退出', keyName: 'ctrlD', title: 'Ctrl+D — exit session (EOF)' },
  { label: 'Tab', caption: '补全', keyName: 'tab', title: 'Tab — complete / toggle' },
  { label: '←', caption: '左移', keyName: 'arrowLeft', title: 'Left — move cursor left' },
  { label: '→', caption: '右移', keyName: 'arrowRight', title: 'Right — move cursor right' },
  { label: '/', caption: '命令', keyName: 'slash', title: 'Slash — command launcher' },
]

/**
 * Options for mountKeybar (A2 voice extension).
 *
 * All fields are optional so existing callers that pass only `onSend`
 * continue to work unchanged.
 */
export interface KeybarOpts {
  /**
   * Push-to-talk callback.  Called with 'start' on touchstart/mousedown and
   * 'stop' on touchend/mouseup.  The 🎤 button is only rendered when speech
   * recognition is supported by the browser AND this callback is supplied.
   *
   * SEC-L2: Chrome Web Speech forwards audio to Google's servers — the button
   * title discloses this so the user can make an informed choice.
   */
  onVoiceTrigger?: (action: 'start' | 'stop') => void
}

/**
 * Mount the key bar: create buttons in #keybar, bind touchstart → onSend(bytes) +
 * preventDefault (keeps the soft keyboard hidden on mobile).
 *
 * A2: when SpeechRecognition is supported and opts.onVoiceTrigger is provided,
 * appends a 🎤 push-to-talk button at the end of the bar.
 */
export function mountKeybar(onSend: (data: string) => void, opts?: KeybarOpts): void {
  const keybarEl = document.getElementById('keybar')
  if (!keybarEl) return

  for (const { label, caption, keyName, title, primary } of KEYBAR_BUTTONS) {
    const btn = document.createElement('button')
    btn.classList.add('keybar-btn')
    if (primary) btn.classList.add('keybar-btn-primary')
    btn.dataset.key = keyName
    btn.title = title
    btn.setAttribute('aria-label', title)

    const keyEl = document.createElement('span')
    keyEl.className = 'kb-key'
    keyEl.textContent = label
    const capEl = document.createElement('span')
    capEl.className = 'kb-cap'
    capEl.textContent = caption
    btn.append(keyEl, capEl)

    const bytes = KEY_MAP[keyName]

    // touchstart: send immediately, prevent default so the soft keyboard stays hidden.
    btn.addEventListener('touchstart', (e) => {
      e.preventDefault()
      onSend(bytes)
    })

    // Desktop fallback: click also sends.
    btn.addEventListener('click', (e) => {
      e.preventDefault()
      onSend(bytes)
    })

    keybarEl.appendChild(btn)
  }

  // A2: voice mic button — only when speech is supported and caller provides a trigger.
  if (isSpeechSupported() && opts?.onVoiceTrigger) {
    keybarEl.appendChild(buildMicButton(opts.onVoiceTrigger))
  }
}

/**
 * Build the 🎤 push-to-talk button element.
 * SEC-L2: title discloses that Chrome sends audio to Google.
 */
function buildMicButton(onVoiceTrigger: (action: 'start' | 'stop') => void): HTMLButtonElement {
  const btn = document.createElement('button')
  btn.classList.add('keybar-btn')
  btn.dataset.key = 'voice'
  btn.title = 'Hold to dictate — audio processed by browser speech engine (Chrome: Google)'
  btn.setAttribute('aria-label', 'Push to talk')

  const keyEl = document.createElement('span')
  keyEl.className = 'kb-key'
  keyEl.textContent = '🎤'
  const capEl = document.createElement('span')
  capEl.className = 'kb-cap'
  capEl.textContent = '语音'
  btn.append(keyEl, capEl)

  // push-to-talk: start on press, stop on release — preventDefault keeps soft keyboard hidden.
  btn.addEventListener('touchstart', (e) => {
    e.preventDefault()
    onVoiceTrigger('start')
  })
  btn.addEventListener('touchend', (e) => {
    e.preventDefault()
    onVoiceTrigger('stop')
  })
  // Desktop fallback: mousedown/mouseup for push-to-talk semantics.
  btn.addEventListener('mousedown', (e) => {
    e.preventDefault()
    onVoiceTrigger('start')
  })
  btn.addEventListener('mouseup', (e) => {
    e.preventDefault()
    onVoiceTrigger('stop')
  })

  return btn
}
