import { describe, expect, it } from 'vitest';
import { createEtaTracker, formatEta } from './transferEta';

function trackerAt(times) {
  let i = 0;
  const clock = () => times[i++];
  return createEtaTracker(clock);
}

describe('createEtaTracker', () => {
  it('gives no estimate before two seconds of samples exist', () => {
    const tracker = trackerAt([0, 1000]);
    tracker.update(0);
    expect(tracker.remainingSeconds(0, 1000)).toBeNull(); // one sample
    tracker.update(100);
    expect(tracker.remainingSeconds(100, 1000)).toBeNull(); // 1s span < 2s
  });

  it('estimates from the byte rate over the sample window', () => {
    const tracker = trackerAt([0, 5000, 10_000]);
    tracker.update(0);
    tracker.update(50); // 50 bytes in 5s = 10 B/s
    tracker.update(100); // 100 bytes in 10s = 10 B/s
    // 900 bytes left at 10 B/s = 90s.
    expect(tracker.remainingSeconds(100, 1000)).toBeCloseTo(90);
  });

  it('adapts to the recent rate, not the whole transfer history', () => {
    // 0..20s at 10 B/s, then 25..45s at 1000 B/s — the old slow samples age
    // out of the 20s window, so the estimate reflects the fast link.
    const times = [0, 10_000, 20_000, 25_000, 35_000, 45_000];
    const tracker = trackerAt(times);
    tracker.update(0);
    tracker.update(100);
    tracker.update(200);
    tracker.update(5200);
    tracker.update(15_200);
    tracker.update(25_200);
    const remaining = tracker.remainingSeconds(25_200, 125_200);
    // 100,000 bytes left at ~1000 B/s ≈ 100s (allow window-edge slop).
    expect(remaining).toBeGreaterThan(60);
    expect(remaining).toBeLessThan(140);
  });

  it('returns null for unknown totals, stalled links, and finished transfers', () => {
    const tracker = trackerAt([0, 5000]);
    tracker.update(500);
    tracker.update(500); // no bytes moved
    expect(tracker.remainingSeconds(500, 0)).toBeNull(); // unknown total
    expect(tracker.remainingSeconds(500, 1000)).toBeNull(); // zero rate
    expect(tracker.remainingSeconds(1000, 1000)).toBeNull(); // done
  });
});

describe('formatEta', () => {
  it('formats seconds into coarse human buckets', () => {
    expect(formatEta(null)).toBeNull();
    expect(formatEta(12)).toBe('under a minute');
    expect(formatEta(200)).toBe('about 3 min');
    expect(formatEta(4860)).toBe('about 1 h 21 min');
    expect(formatEta(7200)).toBe('about 2 h');
  });
});
