import { describe, expect, test, vi } from 'vitest';
import { flushSync } from 'svelte';
import { useDelayed } from './useDelayed.svelte';

describe('useDelayed', () => {
  test('value stays false until delay elapses while source is true', () => {
    vi.useFakeTimers();
    try {
      let source = $state(false);
      let delayed!: { readonly value: boolean };
      const cleanup = $effect.root(() => {
        delayed = useDelayed(() => source, 100);
      });

      expect(delayed.value).toBe(false);

      source = true;
      flushSync();
      expect(delayed.value).toBe(false);

      vi.advanceTimersByTime(50);
      expect(delayed.value).toBe(false);

      vi.advanceTimersByTime(60);
      expect(delayed.value).toBe(true);

      cleanup();
    } finally {
      vi.useRealTimers();
    }
  });

  test('source flipping false before delay cancels the pending timeout', () => {
    vi.useFakeTimers();
    try {
      let source = $state(false);
      let delayed!: { readonly value: boolean };
      const cleanup = $effect.root(() => {
        delayed = useDelayed(() => source, 100);
      });

      source = true;
      flushSync();
      vi.advanceTimersByTime(40);
      source = false;
      flushSync();
      vi.advanceTimersByTime(200);
      expect(delayed.value).toBe(false);

      cleanup();
    } finally {
      vi.useRealTimers();
    }
  });

  test('value resets to false immediately when source becomes false', () => {
    vi.useFakeTimers();
    try {
      let source = $state(true);
      let delayed!: { readonly value: boolean };
      const cleanup = $effect.root(() => {
        delayed = useDelayed(() => source, 100);
      });
      flushSync(); // run the initial $effect so the setTimeout is scheduled

      vi.advanceTimersByTime(200);
      expect(delayed.value).toBe(true);

      source = false;
      flushSync();
      expect(delayed.value).toBe(false);

      cleanup();
    } finally {
      vi.useRealTimers();
    }
  });
});
