import assert from 'node:assert/strict';
import { test } from 'node:test';
import { compareBuilds, helloAction, mayAutoReload, retryDelay, within } from '../src/lib/engine-build.ts';

test('builds order by timestamp then tag', () => {
  assert.ok((compareBuilds('200-aaa', '100-zzz') ?? 0) > 0);
  assert.ok((compareBuilds('100-aaa', '200-aaa') ?? 0) < 0);
  assert.ok((compareBuilds('100-abc', '100-abd') ?? 0) < 0);
  assert.equal(compareBuilds('100-abc', '100-abc'), 0);
});

test('timestamps compare numerically, not as strings', () => {
  assert.ok((compareBuilds('1000-a', '999-a') ?? 0) > 0);
});

test('malformed build ids never order', () => {
  assert.equal(compareBuilds('dev', '100-a'), null);
  assert.equal(compareBuilds('100-a', ''), null);
  assert.equal(helloAction('100-a', 'garbage'), 'ignore');
});

test('a worker yields to a newer peer', () => {
  assert.equal(helloAction('100-a', '200-b'), 'yield');
});

test('a worker announces itself to an older peer', () => {
  assert.equal(helloAction('200-b', '100-a'), 'announce');
});

test('the same build coexists', () => {
  assert.equal(helloAction('100-a', '100-a'), 'ignore');
});

test('two workers never both yield', () => {
  const pairs: [string, string][] = [['100-a', '200-b'], ['100-b', '100-a'], ['5-x', '50-x']];
  for (const [one, two] of pairs) {
    const actions = [helloAction(one, two), helloAction(two, one)];
    assert.equal(actions.filter((a) => a === 'yield').length, 1);
  }
});

test('auto reload is limited to once per guard window', () => {
  assert.equal(mayAutoReload(1_000_000, null), true);
  assert.equal(mayAutoReload(1_030_000, 1_000_000), false);
  assert.equal(mayAutoReload(1_060_000, 1_000_000), true);
});

test('retry delay backs off and caps', () => {
  assert.deepEqual([0, 1, 2, 3, 4, 10].map(retryDelay), [1000, 2000, 4000, 8000, 8000, 8000]);
});

test('a release wait ends when the engine answers, fails, or goes quiet', async () => {
  const start = Date.now();
  await within(Promise.resolve(), 5_000);
  await within(Promise.reject(new Error('gone')), 5_000);
  assert.ok(Date.now() - start < 1_000);
  await within(new Promise(() => undefined), 20);
});
