// Tests for Electrobun keybinding-store — pure logic.
// Uses bun:test. Tests default bindings, chord serialization, conflict detection.

import { describe, it, expect, beforeEach } from 'bun:test';

// ── Replicated types ──────────────────────────────────────────────────────────

interface Keybinding {
  id: string;
  label: string;
  category: 'Global' | 'Navigation' | 'Terminal' | 'Settings';
  chord: string;
  defaultChord: string;
}

// ── Default bindings (replicated from keybinding-store.svelte.ts) ───────────

const DEFAULTS: Keybinding[] = [
  { id: 'palette',       label: 'Command Palette',       category: 'Global',     chord: 'Ctrl+K',       defaultChord: 'Ctrl+K'       },
  { id: 'settings',      label: 'Open Settings',          category: 'Global',     chord: 'Ctrl+,',       defaultChord: 'Ctrl+,'       },
  { id: 'group1',        label: 'Switch to Group 1',      category: 'Navigation', chord: 'Ctrl+1',       defaultChord: 'Ctrl+1'       },
  { id: 'group2',        label: 'Switch to Group 2',      category: 'Navigation', chord: 'Ctrl+2',       defaultChord: 'Ctrl+2'       },
  { id: 'group3',        label: 'Switch to Group 3',      category: 'Navigation', chord: 'Ctrl+3',       defaultChord: 'Ctrl+3'       },
  { id: 'group4',        label: 'Switch to Group 4',      category: 'Navigation', chord: 'Ctrl+4',       defaultChord: 'Ctrl+4'       },
  { id: 'newTerminal',   label: 'New Terminal Tab',        category: 'Terminal',   chord: 'Ctrl+Shift+T', defaultChord: 'Ctrl+Shift+T' },
  { id: 'closeTab',      label: 'Close Terminal Tab',      category: 'Terminal',   chord: 'Ctrl+Shift+W', defaultChord: 'Ctrl+Shift+W' },
  { id: 'nextTab',       label: 'Next Terminal Tab',       category: 'Terminal',   chord: 'Ctrl+]',       defaultChord: 'Ctrl+]'       },
  { id: 'prevTab',       label: 'Previous Terminal Tab',   category: 'Terminal',   chord: 'Ctrl+[',       defaultChord: 'Ctrl+['       },
  { id: 'search',        label: 'Global Search',           category: 'Global',     chord: 'Ctrl+Shift+F', defaultChord: 'Ctrl+Shift+F' },
  { id: 'notifications', label: 'Notification Center',     category: 'Global',     chord: 'Ctrl+Shift+N', defaultChord: 'Ctrl+Shift+N' },
  { id: 'minimize',      label: 'Minimize Window',         category: 'Global',     chord: 'Ctrl+M',       defaultChord: 'Ctrl+M'       },
  { id: 'toggleFiles',   label: 'Toggle Files Tab',        category: 'Navigation', chord: 'Ctrl+Shift+E', defaultChord: 'Ctrl+Shift+E' },
  { id: 'toggleMemory',  label: 'Toggle Memory Tab',       category: 'Navigation', chord: 'Ctrl+Shift+M', defaultChord: 'Ctrl+Shift+M' },
  { id: 'reload',        label: 'Reload App',              category: 'Settings',   chord: 'Ctrl+R',       defaultChord: 'Ctrl+R'       },
];

// ── Chord serialization (replicated) ─────────────────────────────────────────

interface MockKeyboardEvent {
  ctrlKey: boolean;
  metaKey: boolean;
  shiftKey: boolean;
  altKey: boolean;
  key: string;
}

function chordFromEvent(e: MockKeyboardEvent): string {
  const parts: string[] = [];
  if (e.ctrlKey || e.metaKey) parts.push('Ctrl');
  if (e.shiftKey) parts.push('Shift');
  if (e.altKey) parts.push('Alt');
  const key = e.key === ' ' ? 'Space' : e.key;
  if (!['Control', 'Shift', 'Alt', 'Meta'].includes(key)) {
    parts.push(key.length === 1 ? key.toUpperCase() : key);
  }
  return parts.join('+');
}

// ── Store logic (replicated without runes) ──────────────────────────────────

function createKeybindingState() {
  let bindings: Keybinding[] = DEFAULTS.map(b => ({ ...b }));

  return {
    getBindings: () => bindings,
    setChord(id: string, chord: string): void {
      bindings = bindings.map(b => b.id === id ? { ...b, chord } : b);
    },
    resetChord(id: string): void {
      const def = DEFAULTS.find(b => b.id === id);
      if (!def) return;
      bindings = bindings.map(b => b.id === id ? { ...b, chord: def.defaultChord } : b);
    },
    findConflicts(chord: string, excludeId?: string): Keybinding[] {
      return bindings.filter(b => b.chord === chord && b.id !== excludeId);
    },
  };
}

// ── Tests ───────────────────────────────────────────────────────────────────

describe('default bindings', () => {
  it('has exactly 16 default bindings', () => {
    expect(DEFAULTS).toHaveLength(16);
  });

  it('all bindings have unique ids', () => {
    const ids = DEFAULTS.map(b => b.id);
    expect(new Set(ids).size).toBe(ids.length);
  });

  it('all chords match defaultChord initially', () => {
    for (const b of DEFAULTS) {
      expect(b.chord).toBe(b.defaultChord);
    }
  });

  it('covers all 4 categories', () => {
    const categories = new Set(DEFAULTS.map(b => b.category));
    expect(categories.has('Global')).toBe(true);
    expect(categories.has('Navigation')).toBe(true);
    expect(categories.has('Terminal')).toBe(true);
    expect(categories.has('Settings')).toBe(true);
  });

  it('command palette is Ctrl+K', () => {
    const palette = DEFAULTS.find(b => b.id === 'palette');
    expect(palette?.chord).toBe('Ctrl+K');
  });
});

describe('chordFromEvent', () => {
  it('serializes Ctrl+K', () => {
    expect(chordFromEvent({ ctrlKey: true, metaKey: false, shiftKey: false, altKey: false, key: 'k' })).toBe('Ctrl+K');
  });

  it('serializes Ctrl+Shift+F', () => {
    expect(chordFromEvent({ ctrlKey: true, metaKey: false, shiftKey: true, altKey: false, key: 'f' })).toBe('Ctrl+Shift+F');
  });

  it('serializes Alt+1', () => {
    expect(chordFromEvent({ ctrlKey: false, metaKey: false, shiftKey: false, altKey: true, key: '1' })).toBe('Alt+1');
  });

  it('maps space to Space', () => {
    expect(chordFromEvent({ ctrlKey: true, metaKey: false, shiftKey: false, altKey: false, key: ' ' })).toBe('Ctrl+Space');
  });

  it('ignores pure modifier keys', () => {
    expect(chordFromEvent({ ctrlKey: true, metaKey: false, shiftKey: false, altKey: false, key: 'Control' })).toBe('Ctrl');
  });

  it('metaKey treated as Ctrl', () => {
    expect(chordFromEvent({ ctrlKey: false, metaKey: true, shiftKey: false, altKey: false, key: 'k' })).toBe('Ctrl+K');
  });

  it('preserves multi-char key names', () => {
    expect(chordFromEvent({ ctrlKey: false, metaKey: false, shiftKey: false, altKey: false, key: 'Escape' })).toBe('Escape');
  });
});

describe('setChord / resetChord', () => {
  let state: ReturnType<typeof createKeybindingState>;

  beforeEach(() => {
    state = createKeybindingState();
  });

  it('setChord updates the binding', () => {
    state.setChord('palette', 'Ctrl+P');
    const b = state.getBindings().find(b => b.id === 'palette');
    expect(b?.chord).toBe('Ctrl+P');
    expect(b?.defaultChord).toBe('Ctrl+K'); // default unchanged
  });

  it('resetChord restores default', () => {
    state.setChord('palette', 'Ctrl+P');
    state.resetChord('palette');
    const b = state.getBindings().find(b => b.id === 'palette');
    expect(b?.chord).toBe('Ctrl+K');
  });

  it('resetChord ignores unknown id', () => {
    const before = state.getBindings().length;
    state.resetChord('nonexistent');
    expect(state.getBindings().length).toBe(before);
  });
});

describe('conflict detection', () => {
  let state: ReturnType<typeof createKeybindingState>;

  beforeEach(() => {
    state = createKeybindingState();
  });

  it('detects conflict when two bindings share a chord', () => {
    state.setChord('settings', 'Ctrl+K'); // same as palette
    const conflicts = state.findConflicts('Ctrl+K', 'settings');
    expect(conflicts).toHaveLength(1);
    expect(conflicts[0].id).toBe('palette');
  });

  it('no conflict when chord is unique', () => {
    state.setChord('palette', 'Ctrl+Shift+P');
    const conflicts = state.findConflicts('Ctrl+Shift+P', 'palette');
    expect(conflicts).toHaveLength(0);
  });

  it('excludes self from conflict check', () => {
    const conflicts = state.findConflicts('Ctrl+K', 'palette');
    expect(conflicts).toHaveLength(0);
  });

  it('finds multiple conflicts', () => {
    state.setChord('search', 'Ctrl+K');
    state.setChord('reload', 'Ctrl+K');
    const conflicts = state.findConflicts('Ctrl+K', 'settings');
    expect(conflicts).toHaveLength(3); // palette, search, reload
  });
});

describe('capture mode', () => {
  it('chordFromEvent records full chord for capture', () => {
    const chord = chordFromEvent({
      ctrlKey: true, metaKey: false, shiftKey: true, altKey: false, key: 'x',
    });
    expect(chord).toBe('Ctrl+Shift+X');
  });
});

// ── Chord sequence helpers ──────────────────────────────────────────────────

function chordParts(chord: string): string[] {
  return chord.split(' ').filter(Boolean);
}

function formatChord(chord: string): string {
  return chordParts(chord).join(' \u2192 ');
}

describe('chord sequences', () => {
  it('chordParts splits single chord', () => {
    expect(chordParts('Ctrl+K')).toEqual(['Ctrl+K']);
  });

  it('chordParts splits multi-key chord', () => {
    expect(chordParts('Ctrl+K Ctrl+S')).toEqual(['Ctrl+K', 'Ctrl+S']);
  });

  it('formatChord adds arrow separator', () => {
    expect(formatChord('Ctrl+K Ctrl+S')).toBe('Ctrl+K \u2192 Ctrl+S');
  });

  it('formatChord passes through single chord', () => {
    expect(formatChord('Ctrl+Shift+F')).toBe('Ctrl+Shift+F');
  });

  it('setChord stores chord sequence', () => {
    const state = createKeybindingState();
    state.setChord('palette', 'Ctrl+K Ctrl+P');
    const b = state.getBindings().find(b => b.id === 'palette');
    expect(b?.chord).toBe('Ctrl+K Ctrl+P');
  });

  it('conflict detection works with chord sequences', () => {
    const state = createKeybindingState();
    state.setChord('palette', 'Ctrl+K Ctrl+P');
    state.setChord('settings', 'Ctrl+K Ctrl+P');
    const conflicts = state.findConflicts('Ctrl+K Ctrl+P', 'palette');
    expect(conflicts).toHaveLength(1);
    expect(conflicts[0].id).toBe('settings');
  });
});
