/**
 * These helpers provide a consistent way to:
 * - Normalize user-defined hotkey strings (e.g. "cmd+k", "ctrl+shift+p")
 * - Normalize `KeyboardEvent`s into the same canonical hotkey format
 *
 * **Canonical format**
 * - Modifiers are ordered: `meta`, `ctrl`, `alt`, `shift`
 * - Tokens are joined with `+`
 * - The final token is the non-modifier key
 *
 * Examples:
 * - `"cmd+k"` → `"meta+k"`
 * - `"return"` → `"enter"`
 * - `"backspace"` → `"backspace"`
 * - `"alt+shift+l"` → `"alt+shift+l"`
 *
 * Note: `keycodes.ts` defines single-key constants (e.g. `ENTER`, `ESCAPE`)
 * used for simple `event.key` comparisons in components. This module handles
 * multi-key hotkey combinations with modifier normalization and alias
 * resolution — a different concern.
 */
const NORMALIZED_HOTKEY_SEPARATOR = '+';
const KEY_ALIASES = {
    cmd: 'meta',
    command: 'meta',
    win: 'meta',
    windows: 'meta',
    control: 'ctrl',
    option: 'alt',
    esc: 'escape',
    return: 'enter',
    del: 'delete',
    up: 'arrowup',
    down: 'arrowdown',
    left: 'arrowleft',
    right: 'arrowright',
    spacebar: 'space',
};
export const tokenizeHotkeyString = (hotkey) => {
    var _a;
    const raw = (hotkey !== null && hotkey !== void 0 ? hotkey : '').trim();
    if (!raw) {
        return [];
    }
    // Allow `+` as a hotkey.
    if (raw === '+') {
        return ['+'];
    }
    // Split on `+`, but treat `++` as the `+` key.
    const tokens = [];
    let current = '';
    let index = 0;
    while (index < raw.length) {
        const char = raw[index];
        if (char !== '+') {
            current += char;
            index++;
            continue;
        }
        let nextIndex = index + 1;
        while (((_a = raw[nextIndex]) === null || _a === void 0 ? void 0 : _a.trim()) === '') {
            nextIndex++;
        }
        const nextChar = raw[nextIndex];
        if (nextChar === '+') {
            const token = current.trim();
            if (token) {
                tokens.push(token);
            }
            tokens.push('+');
            current = '';
            index = nextIndex + 1;
            continue;
        }
        const token = current.trim();
        if (token) {
            tokens.push(token);
        }
        current = '';
        index++;
    }
    const tail = current.trim();
    if (tail) {
        tokens.push(tail);
    }
    return tokens;
};
const normalizeKey = (key) => {
    const normalized = key.trim().toLowerCase();
    if (!normalized) {
        return null;
    }
    if (Object.hasOwn(KEY_ALIASES, normalized)) {
        return KEY_ALIASES[normalized];
    }
    return normalized;
};
const normalizeModifiersAndKey = (input) => {
    const normalizedKey = normalizeKey(input.key);
    if (!normalizedKey) {
        return null;
    }
    // Ignore pure modifier presses
    if (['shift', 'alt', 'ctrl', 'meta'].includes(normalizedKey)) {
        return null;
    }
    const parts = [];
    if (input.meta) {
        parts.push('meta');
    }
    if (input.ctrl) {
        parts.push('ctrl');
    }
    if (input.alt) {
        parts.push('alt');
    }
    if (input.shift && normalizedKey !== '+') {
        parts.push('shift');
    }
    parts.push(normalizedKey);
    return parts.join(NORMALIZED_HOTKEY_SEPARATOR);
};
/**
 * Normalize a user-defined hotkey string to the canonical format.
 *
 * Returns `null` for empty/invalid inputs or if the string only contains
 * modifiers (e.g. `"ctrl+shift"`).
 *
 * If multiple non-modifier keys are present, only the last one is used
 * (e.g. `"ctrl+a+b"` becomes `"ctrl+b"`).
 *
 * @param hotkey - User-provided hotkey string.
 */
export const normalizeHotkeyString = (hotkey) => {
    if (!hotkey) {
        return null;
    }
    const tokens = tokenizeHotkeyString(hotkey);
    if (tokens.length === 0) {
        return null;
    }
    let alt = false;
    let ctrl = false;
    let meta = false;
    let shift = false;
    let key = null;
    for (const token of tokens) {
        const normalized = normalizeKey(token);
        if (!normalized) {
            continue;
        }
        if (normalized === 'alt') {
            alt = true;
            continue;
        }
        if (normalized === 'ctrl') {
            ctrl = true;
            continue;
        }
        if (normalized === 'meta') {
            meta = true;
            continue;
        }
        if (normalized === 'shift') {
            shift = true;
            continue;
        }
        // Last non-modifier wins
        key = normalized;
    }
    if (!key) {
        return null;
    }
    return normalizeModifiersAndKey({ key, alt, ctrl, meta, shift });
};
const normalizeEventKey = (event) => {
    const code = (event.code || '').trim();
    if (/^Key[A-Z]$/.test(code)) {
        return code.slice(3).toLowerCase();
    }
    if (/^Digit\d$/.test(code)) {
        return code.slice(5);
    }
    const key = event.key;
    if (key === ' ') {
        return 'space';
    }
    return normalizeKey(key);
};
/**
 * Convert a `KeyboardEvent` into a canonical hotkey string.
 *
 * Uses `event.code` when possible for letter/digit keys to avoid
 * layout-dependent results.
 *
 * @param event - Keyboard event to normalize.
 */
export const hotkeyFromKeyboardEvent = (event) => {
    const key = normalizeEventKey(event);
    if (!key) {
        return null;
    }
    // `+` typically requires Shift on many keyboard layouts, but users expect to
    // write hotkeys like `meta++` (⌘+) without explicitly adding `shift`.
    const shift = key === '+' ? false : event.shiftKey;
    return normalizeModifiersAndKey({
        key,
        alt: event.altKey,
        ctrl: event.ctrlKey,
        meta: event.metaKey,
        shift,
    });
};
