import assert from 'node:assert/strict';
import { test } from 'node:test';
import { observeArrivals } from '../src/lib/arrivals.ts';
import type { TimelineEvent, TimelineItem } from '../src/lib/types.ts';

const MINUTE = 60 * 1000;

function event(id: string, timestamp_ms: number, extra: Partial<TimelineEvent> = {}): TimelineItem {
  return { kind: 'event', event: { item_id: id, timestamp_ms, own: false, ...extra } as TimelineEvent };
}

function lagOf(items: TimelineItem[] | null, id: string): number | undefined {
  const found = items?.find((item) => item.kind === 'event' && item.event.item_id === id);
  return found?.kind === 'event' ? found.event.delivery_lag_ms : undefined;
}

test('flags a delayed event appended after newer known events', () => {
  const now = 100 * MINUTE;
  const previous = [event('a', 90 * MINUTE), event('b', 95 * MINUTE)];
  const next = observeArrivals(previous, [...previous, event('late', 80 * MINUTE)], 0, now);
  assert.equal(lagOf(next, 'late'), 20 * MINUTE);
});

test('ignores backfill and prompt arrivals', () => {
  const now = 100 * MINUTE;
  const previous = [event('a', 99 * MINUTE)];
  const next = observeArrivals(previous, [event('old', 10 * MINUTE), ...previous, event('new', now - 1000)], 0, now);
  assert.equal(lagOf(next, 'old'), undefined);
  assert.equal(lagOf(next, 'new'), undefined);
});

test('measures from when the room became live, not from the send time', () => {
  const now = 100 * MINUTE;
  const previous = [event('a', 10 * MINUTE)];
  const caughtUp = observeArrivals(previous, [...previous, event('offline', 20 * MINUTE)], now - 5000, now);
  assert.equal(lagOf(caughtUp, 'offline'), undefined);
  const delayed = observeArrivals(previous, [...previous, event('stuck', 20 * MINUTE)], now - 3 * MINUTE, now);
  assert.equal(lagOf(delayed, 'stuck'), 3 * MINUTE);
});

test('carries a flag across later updates', () => {
  const now = 100 * MINUTE;
  const previous = [event('a', 99 * MINUTE), event('late', 50 * MINUTE, { delivery_lag_ms: 50 * MINUTE })];
  const next = observeArrivals(previous, [...previous.map((item) => item.kind === 'event' ? event(item.event.item_id, item.event.timestamp_ms) : item), event('c', now)], 0, now + MINUTE);
  assert.equal(lagOf(next, 'late'), 50 * MINUTE);
});

test('skips own events and replaced lists', () => {
  const now = 100 * MINUTE;
  const previous = [event('a', 99 * MINUTE)];
  const own = observeArrivals(previous, [...previous, event('mine', 10 * MINUTE, { own: true })], 0, now);
  assert.equal(lagOf(own, 'mine'), undefined);
  assert.equal(observeArrivals(previous, [event('x', 10 * MINUTE)], 0, now), null);
});
