import { describe, expect, it } from 'vitest';

import {
  DEFAULT_SHORTCUT_OPTIONS,
  findReservedConflict,
  findReservedConflicts,
  describeConflictContext,
  normalizeShortcutCombo,
  RESERVED_SHORTCUTS,
  comboToMonacoKeyBinding,
  getPrimaryShortcutDisplayLabel,
  getShortcutDisplayLabel,
  getShortcutPrimaryModifierDisplayLabel,
  resolveShortcutBinding,
  resolveShortcutDisplay,
  sanitizeShortcutOptions,
  SHORTCUT_ACTION_META,
} from './shortcuts';
import type { ConflictInfo } from './shortcuts';

// ─── findReservedConflict ────────────────────────────────────────────

describe('findReservedConflict', () => {
  it('finds Ctrl+F conflict (Monaco Find)', () => {
    const result = findReservedConflict('Ctrl+F');
    expect(result).not.toBeNull();
    expect(result!.label).toBe('编辑器查找');
    expect(result!.context).toBe('monaco');
    expect(result!.monacoCommandId).toBe('actions.find');
  });

  it('finds Ctrl+S conflict (browser save)', () => {
    const result = findReservedConflict('Ctrl+S');
    expect(result).not.toBeNull();
    expect(result!.label).toBe('浏览器保存');
    expect(result!.context).toBe('global');
  });

  it('returns null for non-reserved combo', () => {
    expect(findReservedConflict('Ctrl+Shift+R')).toBeNull();
  });

  it('returns null for empty string', () => {
    expect(findReservedConflict('')).toBeNull();
  });

  it('finds Meta+F (macOS variant)', () => {
    const result = findReservedConflict('Meta+F');
    expect(result).not.toBeNull();
    expect(result!.label).toBe('编辑器查找');
    expect(result!.context).toBe('monaco');
  });

  it('matches after normalization (ctrl+f → Ctrl+F)', () => {
    const result = findReservedConflict(normalizeShortcutCombo('ctrl+f'));
    expect(result).not.toBeNull();
    expect(result!.label).toBe('编辑器查找');
  });

  it('finds F2 conflict', () => {
    const result = findReservedConflict('F2');
    expect(result).not.toBeNull();
    expect(result!.context).toBe('monaco');
  });
});

// ─── findReservedConflicts ───────────────────────────────────────────

describe('findReservedConflicts', () => {
  it('returns multiple conflicts for Ctrl+Enter', () => {
    const results = findReservedConflicts('Ctrl+Enter');
    expect(results.length).toBeGreaterThanOrEqual(1);
    const labels = results.map(r => r.label);
    expect(labels).toContain('编辑器在下方插入行');
  });

  it('returns empty array for non-reserved combo', () => {
    expect(findReservedConflicts('Ctrl+Shift+Q')).toEqual([]);
  });

  it('preserves monacoCommandId in results', () => {
    const results = findReservedConflicts('Ctrl+F');
    expect(results[0].monacoCommandId).toBe('actions.find');
  });

  it('uses Command instead of Control for macOS find shortcut conflicts', () => {
    expect(findReservedConflicts('Ctrl+F', 'mac')).toEqual([]);
    expect(findReservedConflicts('Meta+F', 'mac')[0]).toMatchObject({
      label: '编辑器查找',
      monacoCommandId: 'actions.find',
    });
    expect(findReservedConflicts('Ctrl+F', 'windows')[0]).toMatchObject({
      label: '编辑器查找',
      monacoCommandId: 'actions.find',
    });
  });
});

// ─── describeConflictContext ─────────────────────────────────────────

describe('describeConflictContext', () => {
  it('describes global context', () => {
    expect(describeConflictContext('global')).toBe('浏览器');
  });

  it('describes monaco context', () => {
    expect(describeConflictContext('monaco')).toBe('编辑器');
  });

  it('describes datagrid context', () => {
    expect(describeConflictContext('datagrid')).toBe('数据表格');
  });
});

// ─── RESERVED_SHORTCUTS sanity ───────────────────────────────────────

describe('RESERVED_SHORTCUTS', () => {
  it('all combos are already normalized', () => {
    for (const entry of RESERVED_SHORTCUTS) {
      expect(entry.combo).toBe(normalizeShortcutCombo(entry.combo));
    }
  });

  it('has at least 10 entries', () => {
    expect(RESERVED_SHORTCUTS.length).toBeGreaterThanOrEqual(10);
  });

  it('every entry has a label and context', () => {
    for (const entry of RESERVED_SHORTCUTS) {
      expect(entry.label).toBeTruthy();
      expect(['global', 'monaco', 'datagrid']).toContain(entry.context);
    }
  });
});

// ─── shortcut defaults ───────────────────────────────────────────────

describe('shortcut defaults', () => {
  it('registers select current statement as a query editor shortcut', () => {
    expect(DEFAULT_SHORTCUT_OPTIONS.selectCurrentStatement).toEqual({
      mac: { combo: 'Meta+E', enabled: true },
      windows: { combo: 'Ctrl+E', enabled: true },
    });
    expect(SHORTCUT_ACTION_META.selectCurrentStatement).toMatchObject({
      label: '选择当前语句',
      scope: 'queryEditor',
    });
  });

  it('registers save query as a query editor shortcut', () => {
    expect(DEFAULT_SHORTCUT_OPTIONS.saveQuery).toEqual({
      mac: { combo: 'Meta+S', enabled: true },
      windows: { combo: 'Ctrl+S', enabled: true },
    });
    expect(SHORTCUT_ACTION_META.saveQuery).toMatchObject({
      label: '保存查询',
      scope: 'queryEditor',
      allowInEditable: true,
    });
  });

  it('registers query results panel toggle as a query editor shortcut', () => {
    expect(DEFAULT_SHORTCUT_OPTIONS.toggleQueryResultsPanel).toEqual({
      mac: { combo: 'Meta+Shift+M', enabled: true },
      windows: { combo: 'Ctrl+Shift+M', enabled: true },
    });
    expect(SHORTCUT_ACTION_META.toggleQueryResultsPanel).toMatchObject({
      label: '切换结果区',
      scope: 'queryEditor',
      allowInEditable: true,
    });
  });

  // Windows 任务栏恢复后字体异常变大的兜底入口（方案 3）。
  // 自动 fix 路径（9848b8b2）刻意不再 toggle 以避免可见动画，由该快捷键给用户主动触发的修复入口。
  it('registers reset window zoom shortcut with default Ctrl+Shift+0', () => {
    expect(DEFAULT_SHORTCUT_OPTIONS.resetWindowZoom).toEqual({
      mac: { combo: '', enabled: false },
      windows: { combo: 'Ctrl+Shift+0', enabled: true },
    });
    expect(SHORTCUT_ACTION_META.resetWindowZoom).toMatchObject({
      label: '重置窗口缩放',
      allowInEditable: true,
    });
  });

  it('keeps configurable shortcut descriptions free of hardcoded shortcut labels', () => {
    Object.values(SHORTCUT_ACTION_META).forEach((meta) => {
      expect(meta.description).not.toMatch(/⌘|⌃|Ctrl|Meta|Cmd|Command|Alt\+/);
    });
  });

  it('keeps enabled default shortcuts unique per platform', () => {
    for (const platform of ['mac', 'windows'] as const) {
      const seen = new Map<string, string>();
      Object.entries(DEFAULT_SHORTCUT_OPTIONS).forEach(([action, bindings]) => {
        const binding = bindings[platform];
        if (!binding.enabled || !binding.combo) return;
        const existingAction = seen.get(binding.combo);
        expect(existingAction, `${platform} ${binding.combo} is shared by ${existingAction} and ${action}`).toBeUndefined();
        seen.set(binding.combo, action);
      });
    }
  });

  it('uses Navicat-inspired defaults separately for macOS and Windows/Linux', () => {
    expect(DEFAULT_SHORTCUT_OPTIONS.runQuery).toEqual({
      mac: { combo: 'Meta+R', enabled: true },
      windows: { combo: 'Ctrl+R', enabled: true },
    });
    expect(DEFAULT_SHORTCUT_OPTIONS.newQueryTab).toEqual({
      mac: { combo: 'Meta+N', enabled: true },
      windows: { combo: 'Ctrl+N', enabled: true },
    });
    expect(DEFAULT_SHORTCUT_OPTIONS.switchToNextTab).toEqual({
      mac: { combo: 'Ctrl+Tab', enabled: true },
      windows: { combo: 'Ctrl+Tab', enabled: true },
    });
    expect(DEFAULT_SHORTCUT_OPTIONS.switchToPreviousTab).toEqual({
      mac: { combo: 'Ctrl+Shift+Tab', enabled: true },
      windows: { combo: 'Ctrl+Shift+Tab', enabled: true },
    });
    expect(DEFAULT_SHORTCUT_OPTIONS.toggleLogPanel).toEqual({
      mac: { combo: 'Meta+Shift+H', enabled: true },
      windows: { combo: 'Ctrl+H', enabled: true },
    });
  });

  it('registers connection and AI panel actions as real shortcuts', () => {
    expect(DEFAULT_SHORTCUT_OPTIONS.newConnection).toEqual({
      mac: { combo: 'Meta+Shift+N', enabled: true },
      windows: { combo: 'Ctrl+Shift+N', enabled: true },
    });
    expect(DEFAULT_SHORTCUT_OPTIONS.toggleAIPanel).toEqual({
      mac: { combo: 'Meta+J', enabled: true },
      windows: { combo: 'Ctrl+J', enabled: true },
    });
    expect(SHORTCUT_ACTION_META.newConnection.label).toBe('新建数据源');
    expect(SHORTCUT_ACTION_META.toggleAIPanel.label).toBe('打开 AI 数据洞察');
  });

  it('migrates legacy single-platform shortcut bindings into both platform slots', () => {
    const options = sanitizeShortcutOptions({
      runQuery: { combo: 'Ctrl+Shift+R', enabled: false },
    });

    expect(options.runQuery).toEqual({
      mac: { combo: 'Ctrl+Shift+R', enabled: false },
      windows: { combo: 'Ctrl+Shift+R', enabled: false },
    });
    expect(options.newQueryTab.windows.combo).toBe('Ctrl+N');
  });

  it('sanitizes partial platform shortcut bindings without losing defaults', () => {
    const options = sanitizeShortcutOptions({
      newQueryTab: {
        mac: { combo: 'Meta+N', enabled: false },
      },
      sendAIChatMessage: {
        windows: { combo: 'A', enabled: true },
      },
    });

    expect(options.newQueryTab.mac).toEqual({ combo: 'Meta+N', enabled: false });
    expect(options.newQueryTab.windows).toEqual({ combo: 'Ctrl+N', enabled: true });
    expect(options.saveQuery.windows).toEqual({ combo: 'Ctrl+S', enabled: true });
    expect(options.sendAIChatMessage.windows).toEqual({ combo: 'Enter', enabled: true });
  });

  it('resolves and displays platform-specific bindings', () => {
    const options = sanitizeShortcutOptions({
      newQueryTab: {
        mac: { combo: 'Meta+N', enabled: true },
        windows: { combo: 'Ctrl+N', enabled: true },
      },
    });

    expect(resolveShortcutBinding(options, 'newQueryTab', 'mac')).toEqual({
      combo: 'Meta+N',
      enabled: true,
    });
    expect(getShortcutDisplayLabel('Meta+N', 'mac')).toBe('⌘N');
    expect(getShortcutDisplayLabel('Meta+Shift+H', 'mac')).toBe('⌘⇧H');
    expect(getShortcutDisplayLabel('Ctrl+Meta+F', 'mac')).toBe('⌃⌘F');
    expect(getShortcutDisplayLabel('Meta+S', 'mac')).toBe('⌘S');
    expect(getShortcutDisplayLabel('Ctrl+S', 'windows')).toBe('Ctrl+S');
    expect(getShortcutPrimaryModifierDisplayLabel('mac')).toBe('⌘');
    expect(getShortcutPrimaryModifierDisplayLabel('windows')).toBe('Ctrl');
    expect(getPrimaryShortcutDisplayLabel('C', 'mac')).toBe('⌘C');
    expect(getPrimaryShortcutDisplayLabel('C', 'windows')).toBe('Ctrl+C');
    expect(getPrimaryShortcutDisplayLabel('Enter', 'mac')).toBe('⌘↵');
    expect(getPrimaryShortcutDisplayLabel('Enter', 'windows')).toBe('Ctrl+Enter');
    expect(resolveShortcutDisplay(options, 'newQueryTab', 'windows')).toBe('Ctrl+N');
  });
});

// ─── comboToMonacoKeyBinding ─────────────────────────────────────────

describe('comboToMonacoKeyBinding', () => {
  const mockKeyMod = {
    CtrlCmd: 2048,
    WinCtrl: 256,
    Alt: 512,
    Shift: 1024,
  };

  const mockKeyCode = {
    Enter: 3,
    Tab: 2,
    Escape: 9,
    Space: 10,
    Backspace: 1,
    Delete: 20,
    Home: 14,
    End: 13,
    PageUp: 11,
    PageDown: 12,
    UpArrow: 16,
    DownArrow: 17,
    LeftArrow: 15,
    RightArrow: 18,
    Insert: 19,
    KeyA: 31, KeyB: 32, KeyC: 33, KeyD: 34, KeyE: 35,
    KeyF: 41,
    KeyG: 42, KeyH: 43, KeyK: 47, KeyN: 50, KeyP: 52, KeyR: 54, KeyS: 55,
    Digit0: 21, Digit1: 22, Digit2: 23, Digit3: 24, Digit4: 25,
    Digit5: 26, Digit6: 27, Digit7: 28, Digit8: 29, Digit9: 30,
    F1: 61, F2: 62, F3: 63, F4: 64, F5: 65, F6: 66,
    F7: 67, F8: 68, F9: 69, F10: 70, F11: 71, F12: 72,
    Oem1: 80, Oem2: 81, Oem3: 82, Oem4: 83, Oem5: 84,
    Oem6: 85, Oem7: 86, OemComma: 87, OemMinus: 88,
    OemPlus: 89, OemPeriod: 90,
  };

  it('maps Ctrl+Enter correctly', () => {
    expect(comboToMonacoKeyBinding('Ctrl+Enter', mockKeyMod, mockKeyCode)).toEqual({
      keyMod: mockKeyMod.CtrlCmd,
      keyCode: mockKeyCode.Enter,
    });
  });

  it('maps Ctrl+Shift+R correctly', () => {
    expect(comboToMonacoKeyBinding('Ctrl+Shift+R', mockKeyMod, mockKeyCode)).toEqual({
      keyMod: mockKeyMod.CtrlCmd | mockKeyMod.Shift,
      keyCode: mockKeyCode.KeyR,
    });
  });

  it('maps Meta+Enter (macOS variant)', () => {
    expect(comboToMonacoKeyBinding('Meta+Enter', mockKeyMod, mockKeyCode)).toEqual({
      keyMod: mockKeyMod.WinCtrl,
      keyCode: mockKeyCode.Enter,
    });
  });

  it('maps F2 key', () => {
    expect(comboToMonacoKeyBinding('F2', mockKeyMod, mockKeyCode)).toEqual({
      keyMod: 0,
      keyCode: mockKeyCode.F2,
    });
  });

  it('maps Ctrl+, (comma)', () => {
    expect(comboToMonacoKeyBinding('Ctrl+,', mockKeyMod, mockKeyCode)).toEqual({
      keyMod: mockKeyMod.CtrlCmd,
      keyCode: mockKeyCode.OemComma,
    });
  });

  it('returns null for empty combo', () => {
    expect(comboToMonacoKeyBinding('', mockKeyMod, mockKeyCode)).toBeNull();
  });

  it('returns null for combo with only modifiers', () => {
    expect(comboToMonacoKeyBinding('Ctrl+Shift', mockKeyMod, mockKeyCode)).toBeNull();
  });

  it('maps Ctrl+Digit1', () => {
    expect(comboToMonacoKeyBinding('Ctrl+1', mockKeyMod, mockKeyCode)).toEqual({
      keyMod: mockKeyMod.CtrlCmd,
      keyCode: mockKeyCode.Digit1,
    });
  });

  it('maps Ctrl+Alt+Delete', () => {
    expect(comboToMonacoKeyBinding('Ctrl+Alt+Delete', mockKeyMod, mockKeyCode)).toEqual({
      keyMod: mockKeyMod.CtrlCmd | mockKeyMod.Alt,
      keyCode: mockKeyCode.Delete,
    });
  });
});
