import assert from 'node:assert/strict';
import { test } from 'node:test';
import { PressTracker, type Timers } from '../src/lib/longpress.ts';

function clock() {
  let now = 0;
  let next = 1;
  const queue = new Map<number, { at: number; fn: () => void }>();
  const timers: Timers = {
    set(fn, ms) {
      const id = next++;
      queue.set(id, { at: now + ms, fn });
      return id;
    },
    clear(handle) {
      queue.delete(handle as number);
    }
  };
  const advance = (ms: number) => {
    now += ms;
    for (const [id, t] of [...queue]) {
      if (t.at <= now) {
        queue.delete(id);
        t.fn();
      }
    }
  };
  return { timers, advance };
}

function setup() {
  const c = clock();
  const fires: Array<[number, number]> = [];
  const tracker = new PressTracker((x, y) => fires.push([x, y]), c.timers);
  return { ...c, tracker, fires };
}

test('fires with the press origin after the delay', () => {
  const { tracker, advance, fires } = setup();
  tracker.start(10, 20);
  advance(449);
  assert.deepEqual(fires, []);
  advance(1);
  assert.deepEqual(fires, [[10, 20]]);
  assert.equal(tracker.fired, true);
});

test('release before the delay cancels', () => {
  const { tracker, advance, fires } = setup();
  tracker.start(0, 0);
  advance(200);
  tracker.cancel();
  advance(1000);
  assert.deepEqual(fires, []);
  assert.equal(tracker.fired, false);
});

test('small jitter is tolerated, a drag cancels', () => {
  const a = setup();
  a.tracker.start(0, 0);
  a.tracker.move(4, 4);
  a.advance(450);
  assert.equal(a.fires.length, 1);
  const b = setup();
  b.tracker.start(0, 0);
  b.tracker.move(9, 0);
  b.advance(450);
  assert.equal(b.fires.length, 0);
});

test('a new press clears the fired flag', () => {
  const { tracker, advance } = setup();
  tracker.start(0, 0);
  advance(450);
  tracker.start(5, 5);
  assert.equal(tracker.fired, false);
  assert.equal(tracker.pending, true);
});

test('flush fires a pending press once and ignores an idle tracker', () => {
  const { tracker, advance, fires } = setup();
  tracker.flush();
  assert.equal(fires.length, 0);
  tracker.start(3, 4);
  tracker.flush();
  advance(1000);
  assert.deepEqual(fires, [[3, 4]]);
  assert.equal(tracker.fired, true);
});
