import { describe, expect, test, beforeEach, afterEach, vi } from 'vitest';

// Each test loads a fresh module so module-scope state (_toast, _timer,
// _next) doesn't leak between cases. vi.useFakeTimers() controls the
// auto-clear timeout deterministically; we never depend on real wall time.

beforeEach(() => {
  vi.resetModules();
  vi.useFakeTimers();
});

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

describe('pushToast / clearToast', () => {
  test('pushToast sets value with default variant info', async () => {
    const mod = await import('./toast.svelte');
    mod.pushToast('hello');
    expect(mod.toast.value).not.toBeNull();
    expect(mod.toast.value?.message).toBe('hello');
    expect(mod.toast.value?.variant).toBe('info');
  });

  test('pushToast respects explicit variant error', async () => {
    const mod = await import('./toast.svelte');
    mod.pushToast('boom', 'error');
    expect(mod.toast.value?.variant).toBe('error');
    expect(mod.toast.value?.message).toBe('boom');
  });

  test('pushToast auto-clears after default 5000ms', async () => {
    const mod = await import('./toast.svelte');
    mod.pushToast('temporary');
    expect(mod.toast.value).not.toBeNull();
    vi.advanceTimersByTime(4999);
    expect(mod.toast.value).not.toBeNull();
    vi.advanceTimersByTime(1);
    expect(mod.toast.value).toBeNull();
  });

  test('pushToast respects custom duration', async () => {
    const mod = await import('./toast.svelte');
    mod.pushToast('quick', 'info', 1000);
    vi.advanceTimersByTime(999);
    expect(mod.toast.value).not.toBeNull();
    vi.advanceTimersByTime(1);
    expect(mod.toast.value).toBeNull();
  });

  test('a second push replaces the first and only the second timer fires', async () => {
    const mod = await import('./toast.svelte');
    mod.pushToast('first');
    vi.advanceTimersByTime(2000);
    mod.pushToast('second');
    expect(mod.toast.value?.message).toBe('second');
    // 4000ms after the second push, only 6000ms have elapsed total —
    // the first push's original 5000ms timer must NOT have fired
    // (which would have cleared the second toast).
    vi.advanceTimersByTime(4000);
    expect(mod.toast.value?.message).toBe('second');
    // Now run out the second's clock.
    vi.advanceTimersByTime(1000);
    expect(mod.toast.value).toBeNull();
  });

  test('clearToast resets value and timer', async () => {
    const mod = await import('./toast.svelte');
    mod.pushToast('present');
    expect(mod.toast.value).not.toBeNull();
    mod.clearToast();
    expect(mod.toast.value).toBeNull();
    // Timer was cleared — advancing past the original duration must not
    // re-clear or otherwise touch state in a way that would surface here.
    vi.advanceTimersByTime(10_000);
    expect(mod.toast.value).toBeNull();
  });

  test('each push increments the id (drives {#key} re-mount)', async () => {
    const mod = await import('./toast.svelte');
    mod.pushToast('a');
    const firstId = mod.toast.value?.id;
    mod.pushToast('b');
    const secondId = mod.toast.value?.id;
    mod.pushToast('c');
    const thirdId = mod.toast.value?.id;
    expect(firstId).toBeDefined();
    expect(secondId).toBeDefined();
    expect(thirdId).toBeDefined();
    expect(secondId!).toBeGreaterThan(firstId!);
    expect(thirdId!).toBeGreaterThan(secondId!);
  });
});
