import { describe, it, expect, vi, beforeEach } from 'vitest'
import type { CategoryType } from '~/modules/catalog/types/category'
import type { HydraCollection } from '~/shared/utils/api'

// Mock du store auth : useCategoriesAdmin s'auto-enregistre via
// `onAuthSessionCleared(...)` au chargement du module. On stubbe pour
// eviter de charger Pinia et la vraie store (pas necessaire ici).
vi.mock('~/shared/stores/auth', () => ({
    onAuthSessionCleared: vi.fn(),
}))

// Le client API est un auto-import Nuxt. On le remplace par un stub
// global pour intercepter les appels et controler les reponses dans
// chaque test (cf. pattern utilise dans useCurrentSite.spec.ts).
const mockGet = vi.hoisted(() => vi.fn())
vi.stubGlobal('useApi', () => ({
    get: mockGet,
    post: vi.fn(),
    put: vi.fn(),
    patch: vi.fn(),
    delete: vi.fn(),
}))

// Import APRES vi.mock / vi.stubGlobal : le module n'est evalue qu'a
// ce moment-la, donc le mock auth est bien actif au top-level.
const { useCategoriesAdmin } = await import('../useCategoriesAdmin')

const TYPE_VENTE: CategoryType = { id: 1, code: 'VENTE', label: 'Vente' }
const TYPE_ACHAT: CategoryType = { id: 2, code: 'ACHAT', label: 'Achat' }

function makeHydra<T>(items: T[]): HydraCollection<T> {
    return {
        totalItems: items.length,
        member: items,
    }
}

/**
 * Apres ERP-73, `useCategoriesAdmin` ne porte plus la liste paginee des
 * categories (elle est geree par `usePaginatedList<Category>` cote page).
 * Le composable se concentre sur le referentiel CategoryType (lecture
 * seule, ≤ 5 entrees connues) charge en une fois via `?pagination=false`.
 */
describe('useCategoriesAdmin', () => {
    beforeEach(() => {
        mockGet.mockReset()
        // Reset systematique du state singleton entre tests : sans ca,
        // les types charges dans un test fuiteraient dans le suivant.
        const { resetCategoriesAdmin } = useCategoriesAdmin()
        resetCategoriesAdmin()
    })

    describe('fetchTypes', () => {
        it('appelle GET /category_types avec ?pagination=false (echappatoire selects)', async () => {
            mockGet.mockResolvedValueOnce(makeHydra<CategoryType>([]))
            const { fetchTypes } = useCategoriesAdmin()

            await fetchTypes()

            expect(mockGet).toHaveBeenCalledTimes(1)
            expect(mockGet).toHaveBeenCalledWith(
                '/category_types',
                { pagination: 'false' },
                { toast: false },
            )
        })

        it('peuple types.value depuis le champ Hydra member', async () => {
            mockGet.mockResolvedValueOnce(makeHydra([TYPE_VENTE, TYPE_ACHAT]))
            const { fetchTypes, types } = useCategoriesAdmin()

            await fetchTypes()

            expect(types.value).toEqual([TYPE_VENTE, TYPE_ACHAT])
        })

        it('peuple error.value et vide types en cas d echec', async () => {
            mockGet.mockRejectedValueOnce(new Error('500'))
            const { fetchTypes, types, error, loadingTypes } = useCategoriesAdmin()
            types.value = [TYPE_VENTE]

            await fetchTypes()

            expect(types.value).toEqual([])
            expect(error.value).toContain('500')
            expect(loadingTypes.value).toBe(false)
        })

        it('passe loadingTypes a true pendant la requete et false apres', async () => {
            let resolveRequest: (v: HydraCollection<CategoryType>) => void = () => {}
            mockGet.mockImplementationOnce(
                () => new Promise((resolve) => { resolveRequest = resolve }),
            )
            const { fetchTypes, loadingTypes } = useCategoriesAdmin()

            const pending = fetchTypes()
            expect(loadingTypes.value).toBe(true)

            resolveRequest(makeHydra<CategoryType>([]))
            await pending

            expect(loadingTypes.value).toBe(false)
        })

        it('gere une reponse sans champ member (fallback tableau vide)', async () => {
            mockGet.mockResolvedValueOnce({
                totalItems: 0,
            } as unknown as HydraCollection<CategoryType>)
            const { fetchTypes, types } = useCategoriesAdmin()

            await fetchTypes()

            expect(types.value).toEqual([])
        })
    })

    describe('resetCategoriesAdmin', () => {
        it('vide types, loadingTypes et error', () => {
            const { resetCategoriesAdmin, types, loadingTypes, error }
                = useCategoriesAdmin()
            // Pre-peuple le state pour verifier la purge effective.
            types.value = [TYPE_VENTE]
            loadingTypes.value = true
            error.value = 'oops'

            resetCategoriesAdmin()

            expect(types.value).toEqual([])
            expect(loadingTypes.value).toBe(false)
            expect(error.value).toBeNull()
        })
    })

    describe('singleton', () => {
        it('deux appels a useCategoriesAdmin() partagent la meme ref types', () => {
            const a = useCategoriesAdmin()
            const b = useCategoriesAdmin()

            // Les fonctions sont reinstanciees a chaque appel mais les refs
            // doivent etre rigoureusement les memes (state au niveau module).
            expect(a.types).toBe(b.types)
            expect(a.loadingTypes).toBe(b.loadingTypes)
            expect(a.error).toBe(b.error)
        })

        it('une mutation via une instance est visible depuis une autre instance', () => {
            const a = useCategoriesAdmin()
            const b = useCategoriesAdmin()

            a.types.value = [TYPE_VENTE]

            expect(b.types.value).toEqual([TYPE_VENTE])
        })
    })
})
