import { test } from 'node:test';
import assert from 'node:assert/strict';
import { onTabPress, preventScrollWithArrowKey, onEnterOrSpace, stopPropagation } from './keyboard';

type Evt = {
  key: string;
  which: number;
  altKey: boolean;
  ctrlKey: boolean;
  metaKey: boolean;
  shiftKey: boolean;
  prevented: boolean;
  preventDefault(): void;
};
const evt = (key: string, which: number): Evt => ({
  key,
  which,
  altKey: false,
  ctrlKey: false,
  metaKey: false,
  shiftKey: false,
  prevented: false,
  preventDefault() {
    this.prevented = true;
  },
});

test('onTabPress fires only on Tab', () => {
  let called = 0;
  const tab = evt('Tab', 9);
  onTabPress(tab, () => {
    called += 1;
  });
  assert.equal(called, 1);
  assert.equal(tab.prevented, true);

  const a = evt('a', 65);
  onTabPress(a, () => {
    called += 1;
  });
  assert.equal(called, 1); // unchanged
  assert.equal(a.prevented, false);
});

test('preventScrollWithArrowKey prevents default only on arrows', () => {
  const up = evt('ArrowUp', 38);
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  preventScrollWithArrowKey(up as any);
  assert.equal(up.prevented, true);

  const a = evt('a', 65);
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  preventScrollWithArrowKey(a as any);
  assert.equal(a.prevented, false);
});

test('onEnterOrSpace fires on Enter or Space, not others', () => {
  let count = 0;
  const handler = onEnterOrSpace(() => {
    count += 1;
  });

  const enter = evt('Enter', 13);
  handler(enter);
  assert.equal(count, 1);
  assert.equal(enter.prevented, true);

  const space = evt(' ', 32);
  handler(space);
  assert.equal(count, 2);
  assert.equal(space.prevented, true);

  const other = evt('a', 65);
  handler(other);
  assert.equal(count, 2); // unchanged
  assert.equal(other.prevented, false);
});

test('stopPropagation: stops unless an editable element is focused', () => {
  const makeKeyEvt = () => {
    let stopped = false;
    return {
      ev: {
        stopPropagation() {
          stopped = true;
        },
      },
      wasStopped: () => stopped,
    };
  };
  const withActive = (activeElement: unknown) => {
    (globalThis as { document?: unknown }).document = { activeElement };
  };

  // nothing focused → stops, returns true
  withActive(null);
  let k = makeKeyEvt();
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  assert.equal(stopPropagation(k.ev as any), true);
  assert.equal(k.wasStopped(), true);

  // input focused → does not stop, returns false
  withActive({ nodeName: 'INPUT', getAttribute: () => null });
  k = makeKeyEvt();
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  assert.equal(stopPropagation(k.ev as any), false);
  assert.equal(k.wasStopped(), false);

  // contenteditable focused → does not stop
  withActive({
    nodeName: 'DIV',
    getAttribute: (a: string) => (a === 'contenteditable' ? 'true' : null),
  });
  k = makeKeyEvt();
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  assert.equal(stopPropagation(k.ev as any), false);
});
