import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { setActivePinia, createPinia } from 'pinia';
import axios from 'axios';

vi.mock('axios');

import { useProjectsStore } from '../../resources/js/stores/projectsStore';

describe('projectsStore (no polling)', () => {
    beforeEach(() => {
        setActivePinia(createPinia());
        vi.clearAllMocks();
    });

    it('fetches and stores list', async () => {
        (axios.get as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
            data: { data: [{ id: 1, name: 'X' }], meta: { total: 1, current_page: 1, per_page: 20 } },
        });
        const store = useProjectsStore();
        await store.fetch();
        expect(store.items.length).toBe(1);
        expect(store.total).toBe(1);
    });

    it('passes filters to API', async () => {
        (axios.get as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
            data: { data: [], meta: { total: 0, current_page: 1, per_page: 20 } },
        });
        const store = useProjectsStore();
        store.filters.signal_type = 'site';
        store.filters.search = 'okna';
        await store.fetch();
        expect(axios.get).toHaveBeenCalledWith(
            '/api/projects',
            expect.objectContaining({
                params: expect.objectContaining({ signal_type: 'site', search: 'okna' }),
            }),
        );
    });

    it('create dispatches POST + refetches', async () => {
        (axios.post as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
            data: { data: { id: 2, name: 'New' } },
        });
        (axios.get as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
            data: { data: [{ id: 2 }], meta: { total: 1, current_page: 1, per_page: 20 } },
        });
        const store = useProjectsStore();
        await store.create({
            name: 'New',
            signal_type: 'site',
            signal_identifier: 'x.ru',
            daily_limit_target: 10,
            region_mask: 0,
            region_mode: 'include',
            delivery_days_mask: 127,
        });
        expect(axios.post).toHaveBeenCalled();
    });

    it('toggleSelect adds and removes ids from selectedIds', () => {
        const store = useProjectsStore();
        store.toggleSelect(1);
        expect(store.selectedIds.has(1)).toBe(true);
        store.toggleSelect(1);
        expect(store.selectedIds.has(1)).toBe(false);
    });

    it('del() calls DELETE /api/projects/{id}', async () => {
        const store = useProjectsStore();
        vi.spyOn(axios, 'delete').mockResolvedValue({ data: null });
        vi.spyOn(axios, 'get').mockResolvedValue({ data: { data: [], meta: { total: 0 } } });
        await store.del(7);
        expect(axios.delete).toHaveBeenCalledWith('/api/projects/7');
    });

    it('bulkAction sends array of ids and clears selection', async () => {
        (axios.post as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({ data: { updated: 2 } });
        (axios.get as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
            data: { data: [], meta: { total: 0, current_page: 1, per_page: 20 } },
        });
        const store = useProjectsStore();
        store.selectedIds.add(1);
        store.selectedIds.add(2);
        await store.bulkAction('pause');
        expect(axios.post).toHaveBeenCalledWith('/api/projects/bulk', { action: 'pause', ids: [1, 2] });
        expect(store.selectedIds.size).toBe(0);
    });
});

describe('projectsStore (polling)', () => {
    beforeEach(() => {
        setActivePinia(createPinia());
        vi.useFakeTimers();
        vi.clearAllMocks();
    });

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

    it('starts polling when pendingIds has items', async () => {
        (axios.get as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
            data: {
                data: [
                    {
                        id: 1,
                        sync_status: 'pending',
                        name: 'X',
                        signal_type: 'site',
                        signal_identifier: 'x.ru',
                        daily_limit_target: 10,
                        delivered_today: 0,
                        is_active: true,
                        region_mask: 0,
                        region_mode: 'include',
                        delivery_days_mask: 127,
                    },
                ],
            },
        });
        const store = useProjectsStore();
        store.pendingIds.add(1);
        store.startPolling();
        expect(axios.get).not.toHaveBeenCalled();
        await vi.advanceTimersByTimeAsync(5000);
        expect(axios.get).toHaveBeenCalled();
    });

    it('stops polling when pendingIds becomes empty (sync_status ok)', async () => {
        (axios.get as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
            data: {
                data: [
                    {
                        id: 1,
                        sync_status: 'ok',
                        name: 'X',
                        signal_type: 'site',
                        signal_identifier: 'x.ru',
                        daily_limit_target: 10,
                        delivered_today: 0,
                        is_active: true,
                        region_mask: 0,
                        region_mode: 'include',
                        delivery_days_mask: 127,
                    },
                ],
            },
        });
        const store = useProjectsStore();
        store.pendingIds.add(1);
        store.startPolling();
        await vi.advanceTimersByTimeAsync(5000);
        expect(store.pendingIds.size).toBe(0);
        vi.clearAllMocks();
        await vi.advanceTimersByTimeAsync(5000);
        expect(axios.get).not.toHaveBeenCalled();
    });

    it('exponential backoff on error', async () => {
        (axios.get as unknown as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('network'));
        const store = useProjectsStore();
        store.pendingIds.add(1);
        store.startPolling();
        await vi.advanceTimersByTimeAsync(5000);
        expect(axios.get).toHaveBeenCalledTimes(1);
        await vi.advanceTimersByTimeAsync(10000);
        expect(axios.get).toHaveBeenCalledTimes(2);
        await vi.advanceTimersByTimeAsync(20000);
        expect(axios.get).toHaveBeenCalledTimes(3);
    });
});
