import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { isSilentVisit } from './classify-visit';
import { LoadingCore } from './loading-core';

describe('LoadingCore', () => {
    beforeEach(() => vi.useFakeTimers());
    afterEach(() => vi.useRealTimers());

    function make(opts: Partial<{ showDelay: number; minVisible: number }> = {}) {
        const events: boolean[] = [];
        const core = new LoadingCore((v) => events.push(v), { showDelay: 200, minVisible: 400, ...opts });
        return { core, events };
    }

    it("ne s'affiche pas avant le délai", () => {
        const { core } = make();
        core.start();
        vi.advanceTimersByTime(199);
        expect(core.isVisible).toBe(false);
    });

    it('un appel rapide ne fait jamais flasher la barre', () => {
        const { core, events } = make();
        core.start();
        vi.advanceTimersByTime(100);
        core.done();
        vi.advanceTimersByTime(500);
        expect(core.isVisible).toBe(false);
        expect(events).toEqual([]);
    });

    it("s'affiche après le délai", () => {
        const { core, events } = make();
        core.start();
        vi.advanceTimersByTime(200);
        expect(core.isVisible).toBe(true);
        expect(events).toEqual([true]);
    });

    it('respecte la durée minimale visible', () => {
        const { core } = make();
        core.start();
        vi.advanceTimersByTime(200); // visible à t=200
        core.done(); // elapsed 0 → reste affichée 400ms
        vi.advanceTimersByTime(399);
        expect(core.isVisible).toBe(true);
        vi.advanceTimersByTime(1);
        expect(core.isVisible).toBe(false);
    });

    it('reste visible tant que des tâches concurrentes tournent', () => {
        const { core } = make();
        core.start();
        core.start();
        vi.advanceTimersByTime(200);
        expect(core.isVisible).toBe(true);
        core.done(); // count → 1
        vi.advanceTimersByTime(1000);
        expect(core.isVisible).toBe(true);
        core.done(); // count → 0
        vi.advanceTimersByTime(400);
        expect(core.isVisible).toBe(false);
    });

    it('un nouveau start annule le masquage programmé', () => {
        const { core } = make();
        core.start();
        vi.advanceTimersByTime(600); // visible (shownAt=200), durée min déjà dépassée
        core.done(); // remaining 0 → masquage imminent
        core.start(); // doit annuler le masquage
        vi.advanceTimersByTime(400);
        expect(core.isVisible).toBe(true);
    });

    it('reset masque immédiatement', () => {
        const { core, events } = make();
        core.start();
        vi.advanceTimersByTime(200);
        expect(core.isVisible).toBe(true);
        core.reset();
        expect(core.isVisible).toBe(false);
        expect(events).toEqual([true, false]);
    });
});

describe('isSilentVisit', () => {
    it('rechargement partiel (only) → silencieux', () => {
        expect(isSilentVisit({ only: ['receipts'] })).toBe(true);
    });
    it('visite async → silencieux', () => {
        expect(isSilentVisit({ async: true })).toBe(true);
    });
    it('prefetch → silencieux', () => {
        expect(isSilentVisit({ prefetch: true })).toBe(true);
    });
    it('GET de navigation → visible', () => {
        expect(isSilentVisit({ method: 'get', only: [] })).toBe(false);
    });
    it('soumission POST → visible', () => {
        expect(isSilentVisit({ method: 'post' })).toBe(false);
    });
    it('visite absente → visible (sans erreur)', () => {
        expect(isSilentVisit(null)).toBe(false);
        expect(isSilentVisit(undefined)).toBe(false);
    });
});
