/** biome-ignore-all lint/style/noNonNullAssertion: vendored from OpenTUI — regex match group access after confirmed match */
/** biome-ignore-all lint/suspicious/noControlCharactersInRegex: intentional ESC/CSI pattern matching in terminal escape sequences */
// Copied from https://github.com/enquirer/enquirer/blob/36785f3399a41cd61e9d28d1eb9c2fcd73d69b4c/lib/keypress.js
import { parseKittyKeyboard } from "./parse-keypress-kitty.jsx";
const metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
const fnKeyRe = /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
const keyName = {
  /* xterm/gnome ESC O letter */
  OP: "f1",
  OQ: "f2",
  OR: "f3",
  OS: "f4",
  /* xterm/rxvt ESC [ number ~ */
  "[11~": "f1",
  "[12~": "f2",
  "[13~": "f3",
  "[14~": "f4",
  /* from Cygwin and used in libuv */
  "[[A": "f1",
  "[[B": "f2",
  "[[C": "f3",
  "[[D": "f4",
  "[[E": "f5",
  /* common */
  "[15~": "f5",
  "[17~": "f6",
  "[18~": "f7",
  "[19~": "f8",
  "[20~": "f9",
  "[21~": "f10",
  "[23~": "f11",
  "[24~": "f12",
  "[29~": "menu",
  "[57427~": "clear",
  /* xterm ESC [ letter */
  "[A": "up",
  "[B": "down",
  "[C": "right",
  "[D": "left",
  "[E": "clear",
  "[F": "end",
  "[H": "home",
  "[P": "f1",
  "[Q": "f2",
  "[S": "f4",
  /* xterm/gnome ESC O letter */
  OA: "up",
  OB: "down",
  OC: "right",
  OD: "left",
  OE: "clear",
  OF: "end",
  OH: "home",
  /* xterm/rxvt ESC [ number ~ */
  "[1~": "home",
  "[2~": "insert",
  "[3~": "delete",
  "[4~": "end",
  "[5~": "pageup",
  "[6~": "pagedown",
  /* putty */
  "[[5~": "pageup",
  "[[6~": "pagedown",
  /* rxvt */
  "[7~": "home",
  "[8~": "end",
  /* rxvt keys with modifiers */
  "[a": "up",
  "[b": "down",
  "[c": "right",
  "[d": "left",
  "[e": "clear",
  /* option + arrow keys (old style) */
  f: "right",
  b: "left",
  p: "up",
  n: "down",
  "[2$": "insert",
  "[3$": "delete",
  "[5$": "pageup",
  "[6$": "pagedown",
  "[7$": "home",
  "[8$": "end",
  Oa: "up",
  Ob: "down",
  Oc: "right",
  Od: "left",
  Oe: "clear",
  "[2^": "insert",
  "[3^": "delete",
  "[5^": "pageup",
  "[6^": "pagedown",
  "[7^": "home",
  "[8^": "end",
  /* misc. */
  "[Z": "tab"
};
export const nonAlphanumericKeys = [...Object.values(keyName), "backspace"];
const shiftKeys = new Set(["[a", "[b", "[c", "[d", "[e", "[2$", "[3$", "[5$", "[6$", "[7$", "[8$", "[Z"]);
const isShiftKey = code => shiftKeys.has(code);
const ctrlKeys = new Set(["Oa", "Ob", "Oc", "Od", "Oe", "[2^", "[3^", "[5^", "[6^", "[7^", "[8^"]);
const isCtrlKey = code => ctrlKeys.has(code);
const modifyOtherKeysRe = /^\x1b\[27;(\d+);(\d+)~$/;

// Mouse-event and terminal-response sequences to filter out (not key presses).
const sgrMouseRe = /^\x1b\[<\d+;\d+;\d+[Mm]$/;
const sgrMouseContRe = /^\[<\d+;\d+;\d+[Mm]$/;
const sgrMousePartialRe = /^\x1b\[<[\d;]*$/;
const sgrMousePartialContRe = /^\[<[\d;]*$/;
const sizeReportRe = /^\x1b\[\d+;\d+;\d+t$/;
const cursorPosReportRe = /^\x1b\[\d+;\d+R$/;
const deviceAttrsRe = /^\x1b\[\?[\d;]+c$/;
const modeReportRe = /^\x1b\[\?[\d;]+\$y$/;
const oscResponseRe = /^\x1b\][\d;].*(\x1b\\|\x07)$/;
export const parseKeypress = (s = "", options = {}) => {
  let parts = null;
  if (Buffer.isBuffer(s)) {
    if (s[0] > 127 && s[1] === undefined) {
      s[0] -= 128;
      s = `\x1b${String(s)}`;
    } else {
      s = String(s);
    }
  } else if (s !== undefined && typeof s !== "string") {
    s = String(s);
  } else if (!s) {
    s = "";
  }

  // Every mouse/terminal-response filter below requires s to start with ESC or
  // '['. Plain printable keypresses (the common case) match none of them, so
  // skip the whole regex gauntlet for those.
  const c0 = s.charCodeAt(0);
  if (c0 === 0x1b || c0 === 0x5b /* [ */) {
    // Filter out mouse events (SGR and basic)
    // Complete SGR mouse: ESC[<btn;x;yM or ESC[<btn;x;ym
    if (sgrMouseRe.test(s)) {
      return null;
    }
    // Complete SGR mouse continuation without leading ESC. This can occur when
    // ESC was flushed separately on timeout and the rest of the sequence arrived later.
    if (sgrMouseContRe.test(s)) {
      return null;
    }
    // Incomplete/partial SGR mouse sequences (flushed by the zig parser when
    // a new ESC arrives before the sequence is complete). These start with
    // ESC[< followed by digits/semicolons but lack the terminal M/m.
    if (sgrMousePartialRe.test(s)) {
      return null;
    }
    // Incomplete/partial SGR mouse continuations without ESC.
    if (sgrMousePartialContRe.test(s)) {
      return null;
    }
    if (s.startsWith("\x1b[M") && s.length >= 6) {
      return null;
    }

    // Filter out terminal response sequences (not keyboard events)
    // These are responses to terminal queries and should not be treated as key presses

    // Window/cell size reports: ESC[4;height;width t or ESC[8;rows;cols t
    if (sizeReportRe.test(s)) {
      return null;
    }

    // Cursor position reports (DSR): ESC[row;col R
    if (cursorPosReportRe.test(s)) {
      return null;
    }

    // Device Attributes (DA) responses: ESC[?...c
    if (deviceAttrsRe.test(s)) {
      return null;
    }

    // Mode reports: ESC[?...;...$y
    if (modeReportRe.test(s)) {
      return null;
    }

    // Focus events: ESC[I (focus in), ESC[O (focus out)
    // Note: ESC[O is also used for SS3 sequences (like arrow keys), but those have a character after O
    if (s === "\x1b[I" || s === "\x1b[O") {
      return null;
    }

    // OSC (Operating System Command) responses: ESC]...ESC\ or ESC]...BEL
    if (oscResponseRe.test(s)) {
      return null;
    }

    // Bracketed paste mode markers: ESC[200~ (start), ESC[201~ (end)
    if (s === "\x1b[200~" || s === "\x1b[201~") {
      return null;
    }
  }
  const key = {
    name: "",
    ctrl: false,
    meta: false,
    shift: false,
    option: false,
    number: false,
    sequence: s,
    raw: s,
    eventType: "press",
    source: "raw"
  };
  key.sequence = key.sequence || s || key.name;

  // Check for Kitty keyboard protocol if enabled
  if (options.useKittyKeyboard) {
    const kittyResult = parseKittyKeyboard(s);
    if (kittyResult) {
      return kittyResult;
    }
  }

  // Check for modifyOtherKeys sequences (CSI u protocol variant)
  // Format: CSI 27 ; modifier ; code ~
  // This is sent by terminals (xterm, iTerm2, Ghostty, etc.) with modifyOtherKeys mode enabled
  // to encode modified versions of keys that don't normally have modifier variants
  // Examples: CSI 27;2;13~ (shift+enter), CSI 27;5;13~ (ctrl+enter), CSI 27;5;27~ (ctrl+escape)
  const modifyOtherKeysMatch = modifyOtherKeysRe.exec(s);
  if (modifyOtherKeysMatch) {
    const modifier = parseInt(modifyOtherKeysMatch[1], 10) - 1;
    const charCode = parseInt(modifyOtherKeysMatch[2], 10);
    key.ctrl = !!(modifier & 4);
    key.meta = !!(modifier & 2); // Alt/Option sets meta
    key.shift = !!(modifier & 1);
    key.option = !!(modifier & 2);
    key.super = !!(modifier & 8);
    key.hyper = !!(modifier & 16);

    // Handle common keys by their ASCII codes
    if (charCode === 13) {
      key.name = "return";
    } else if (charCode === 27) {
      key.name = "escape";
    } else if (charCode === 9) {
      key.name = "tab";
    } else if (charCode === 32) {
      key.name = "space";
    } else if (charCode === 127 || charCode === 8) {
      key.name = "backspace";
    } else {
      // For other character codes, use the character itself
      const char = String.fromCharCode(charCode);
      key.name = char;
      key.sequence = char;
      if (charCode >= 48 && charCode <= 57) {
        key.number = true;
      }
    }
    return key;
  }
  if (s === "\r" || s === "\x1b\r") {
    // carriage return
    key.name = "return";
    key.meta = s.length === 2;
  } else if (s === "\n" || s === "\x1b\n") {
    // linefeed
    key.name = "linefeed";
    key.meta = s.length === 2;
  } else if (s === "\t") {
    // tab
    key.name = "tab";
  } else if (s === "\b" || s === "\x1b\b" || s === "\x7f" || s === "\x1b\x7f") {
    // backspace or ctrl+h
    // On OSX, \x7f is also backspace
    key.name = "backspace";
    key.meta = s.charAt(0) === "\x1b";
  } else if (s === "\x1b" || s === "\x1b\x1b") {
    // escape key
    key.name = "escape";
    key.meta = s.length === 2;
  } else if (s === " " || s === "\x1b ") {
    key.name = "space";
    key.meta = s.length === 2;
  } else if (s === "\x00") {
    // ctrl+space
    key.name = "space";
    key.ctrl = true;
  } else if (s.length === 1 && s <= "\x1a") {
    // ctrl+letter
    key.name = String.fromCharCode(s.charCodeAt(0) + "a".charCodeAt(0) - 1);
    key.ctrl = true;
  } else if (s.length === 1 && s >= "0" && s <= "9") {
    // number - keep the actual number character for vim commands
    key.name = s;
    key.number = true;
  } else if (s.length === 1 && s >= "a" && s <= "z") {
    // lowercase letter
    key.name = s;
  } else if (s.length === 1 && s >= "A" && s <= "Z") {
    // shift+letter
    key.name = s.toLowerCase();
    key.shift = true;
  } else if (s.length === 1 || s.length === 2 && s.codePointAt(0) > 0xffff) {
    // Single character (including emoji/surrogate pairs above BMP)
    key.name = s;
  } else if (metaKeyCodeRe.test(s)) {
    // meta+character key
    parts = metaKeyCodeRe.exec(s);
    key.meta = true;
    const char = parts[1];
    const isUpperCase = /^[A-Z]$/.test(char);

    // Check if uppercase F or B map to arrow keys (old terminal style)
    if (char === "F") {
      key.name = "right";
    } else if (char === "B") {
      key.name = "left";
    } else if (isUpperCase) {
      key.shift = true;
      key.name = char;
    } else {
      key.name = char;
    }
  } else if (s.length === 2 && s[0] === "\x1b" && s[1] <= "\x1a") {
    // meta+ctrl+letter (ESC + control character)
    key.meta = true;
    key.ctrl = true;
    key.name = String.fromCharCode(s.charCodeAt(1) + "a".charCodeAt(0) - 1);
  } else if (fnKeyRe.test(s)) {
    parts = fnKeyRe.exec(s);
    const segs = [...s];
    if (segs[0] === "\u001b" && segs[1] === "\u001b") {
      key.option = true;
      key.meta = true;
    }

    // ansi escape sequence
    // reassemble the key code leaving out leading \x1b's,
    // the modifier key bitflag and any meaningless "1;" sequence
    const code = [parts[1], parts[2], parts[4], parts[6]].filter(Boolean).join("");
    const modifier = parseInt(parts[3] || parts[5] || "1", 10) - 1;

    // Parse the key modifier
    // Terminal modifier bits: 1=Shift, 2=Alt/Option, 4=Ctrl, 8=Super, 16=Hyper
    // Note: meta flag is set for Alt/Option (bit 2)
    key.ctrl = key.ctrl || !!(modifier & 4);
    key.meta = key.meta || !!(modifier & 2);
    key.shift = key.shift || !!(modifier & 1);
    key.option = key.option || !!(modifier & 2);
    key.super = !!(modifier & 8);
    key.hyper = !!(modifier & 16);
    key.code = code;
    const keyNameResult = keyName[code];
    if (keyNameResult) {
      key.name = keyNameResult;
      key.shift = isShiftKey(code) || key.shift;
      key.ctrl = isCtrlKey(code) || key.ctrl;
    } else {
      // If we matched the regex but didn't find a valid key name,
      // reset the key to default state (unknown sequence)
      key.name = "";
      key.code = undefined;
    }
  } else if (s === "\x1b[3~") {
    // delete key
    key.name = "delete";
    key.meta = false;
    key.code = "[3~";
  }
  return key;
};
//# sourceMappingURL=parse-keypress.jsx.map
