import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { defineComponent, h } from 'vue';
import { mount } from '@vue/test-utils';
import { usePolling } from '../../resources/js/composables/usePolling';

/**
 * Тесты polling-composable. Используется fake-timers — `setInterval` под
 * контролем `vi.advanceTimersByTime` для детерминированности.
 */

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

afterEach(() => {
    vi.useRealTimers();
    // Сбросим document.hidden если он был установлен.
    Object.defineProperty(document, 'hidden', { value: false, configurable: true, writable: true });
});

function makeHostComponent(loader: () => void, options?: Parameters<typeof usePolling>[1]) {
    return defineComponent({
        setup() {
            usePolling(loader, options);
            return () => h('div');
        },
    });
}

describe('usePolling', () => {
    it('вызывает loader каждые intervalMs миллисекунд', () => {
        const loader = vi.fn();
        const Host = makeHostComponent(loader, { intervalMs: 5000 });
        const wrapper = mount(Host);

        expect(loader).not.toHaveBeenCalled(); // только onMounted, без stale-вызова

        vi.advanceTimersByTime(5000);
        expect(loader).toHaveBeenCalledTimes(1);

        vi.advanceTimersByTime(5000);
        expect(loader).toHaveBeenCalledTimes(2);

        wrapper.unmount();
    });

    it('default intervalMs = 30 секунд', () => {
        const loader = vi.fn();
        const Host = makeHostComponent(loader);
        const wrapper = mount(Host);

        vi.advanceTimersByTime(29_999);
        expect(loader).not.toHaveBeenCalled();

        vi.advanceTimersByTime(1);
        expect(loader).toHaveBeenCalledTimes(1);

        wrapper.unmount();
    });

    it('skip loader когда document.hidden=true', () => {
        const loader = vi.fn();
        const Host = makeHostComponent(loader, { intervalMs: 1000 });
        const wrapper = mount(Host);

        Object.defineProperty(document, 'hidden', { value: true, configurable: true, writable: true });
        vi.advanceTimersByTime(3000);
        expect(loader).not.toHaveBeenCalled();

        Object.defineProperty(document, 'hidden', { value: false, configurable: true, writable: true });
        vi.advanceTimersByTime(1000);
        expect(loader).toHaveBeenCalledTimes(1);

        wrapper.unmount();
    });

    it('cleanup на unmount — больше не вызывает loader', () => {
        const loader = vi.fn();
        const Host = makeHostComponent(loader, { intervalMs: 1000 });
        const wrapper = mount(Host);

        vi.advanceTimersByTime(1000);
        expect(loader).toHaveBeenCalledTimes(1);

        wrapper.unmount();
        vi.advanceTimersByTime(10_000);
        expect(loader).toHaveBeenCalledTimes(1); // больше не растёт
    });

    it('enabled=false — loader НЕ вызывается совсем', () => {
        const loader = vi.fn();
        const Host = makeHostComponent(loader, { intervalMs: 1000, enabled: false });
        const wrapper = mount(Host);

        vi.advanceTimersByTime(10_000);
        expect(loader).not.toHaveBeenCalled();

        wrapper.unmount();
    });

    it('visibilitychange — pause при hidden и resume на возврате с немедленным loader', () => {
        const loader = vi.fn();
        const Host = makeHostComponent(loader, { intervalMs: 5000 });
        const wrapper = mount(Host);

        // Скрываем вкладку — interval останавливается.
        Object.defineProperty(document, 'hidden', { value: true, configurable: true, writable: true });
        document.dispatchEvent(new Event('visibilitychange'));

        vi.advanceTimersByTime(20_000);
        expect(loader).not.toHaveBeenCalled(); // interval остановлен

        // Возвращаемся — должен сразу вызваться loader (без ожидания interval).
        Object.defineProperty(document, 'hidden', { value: false, configurable: true, writable: true });
        document.dispatchEvent(new Event('visibilitychange'));
        expect(loader).toHaveBeenCalledTimes(1);

        // И interval снова работает.
        vi.advanceTimersByTime(5000);
        expect(loader).toHaveBeenCalledTimes(2);

        wrapper.unmount();
    });
});
