import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { debounce } from './debounce';

describe('debounce', () => {
	beforeEach(() => {
		vi.useFakeTimers();
	});

	afterEach(() => {
		vi.useRealTimers();
	});

	it('does not fire before the delay has elapsed', () => {
		const fn = vi.fn();
		const debounced = debounce(fn, 200);

		debounced();
		vi.advanceTimersByTime(199);

		expect(fn).not.toHaveBeenCalled();
	});

	it('fires exactly once after the delay', () => {
		const fn = vi.fn();
		const debounced = debounce(fn, 200);

		debounced();
		vi.advanceTimersByTime(200);

		expect(fn).toHaveBeenCalledTimes(1);
	});

	it('resets the timer on each call — fires only once after inactivity', () => {
		const fn = vi.fn();
		const debounced = debounce(fn, 200);

		debounced();
		vi.advanceTimersByTime(100);
		debounced();
		vi.advanceTimersByTime(100);
		debounced();
		vi.advanceTimersByTime(200);

		expect(fn).toHaveBeenCalledTimes(1);
	});

	it('passes the latest arguments to the callback', () => {
		const fn = vi.fn();
		const debounced = debounce(fn, 200);

		debounced('first');
		debounced('second');
		vi.advanceTimersByTime(200);

		expect(fn).toHaveBeenCalledWith('second');
	});

	it('can fire again after the first invocation settles', () => {
		const fn = vi.fn();
		const debounced = debounce(fn, 200);

		debounced();
		vi.advanceTimersByTime(200);
		debounced();
		vi.advanceTimersByTime(200);

		expect(fn).toHaveBeenCalledTimes(2);
	});
});
