import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { defineComponent, h, ref } from 'vue'

// ── Auto-imports Nuxt stubbes globalement ───────────────────────────────────
// La page ne les importe pas (auto-import) : on les expose en globals pour le
// runtime de test (happy-dom). Meme philosophie que les specs M1/M2/M3.
const mockPush = vi.hoisted(() => vi.fn())
const mockApiGet = vi.hoisted(() => vi.fn())
const mockCan = vi.hoisted(() => vi.fn())
const mockSetFilters = vi.hoisted(() => vi.fn())
const mockFetch = vi.hoisted(() => vi.fn())
const mockToastError = vi.hoisted(() => vi.fn())

vi.stubGlobal('useI18n', () => ({ t: (key: string) => key }))
vi.stubGlobal('useHead', () => undefined)
vi.stubGlobal('useApi', () => ({ get: mockApiGet }))
vi.stubGlobal('useRouter', () => ({ push: mockPush }))
vi.stubGlobal('useToast', () => ({ error: mockToastError, success: vi.fn() }))
vi.stubGlobal('usePermissions', () => ({ can: mockCan }))

// Le repository est lui aussi un auto-import : on controle items + setFilters.
vi.stubGlobal('useCarriersRepository', () => ({
    items: ref([
        {
            id: 7,
            name: 'TRANSPORTS ACME',
            certificationType: 'QUALIMAT',
            qualimatCarrier: { id: '42', name: 'TRANSPORTS ACME', validityDate: '2027-01-15', status: 'VALIDE' },
            updatedAt: '2026-01-15T10:00:00+00:00',
            isArchived: false,
        },
    ]),
    totalItems: ref(1),
    currentPage: ref(1),
    itemsPerPage: ref(10),
    itemsPerPageOptions: ref([10, 25, 50]),
    fetch: mockFetch,
    goToPage: vi.fn(),
    setItemsPerPage: vi.fn(),
    setFilters: mockSetFilters,
}))

// happy-dom n'implemente pas createObjectURL : on ajoute les methodes statiques
// sur la classe URL existante (sans la remplacer — sinon `new URL()` casse).
globalThis.URL.createObjectURL = vi.fn(() => 'blob:fake')
globalThis.URL.revokeObjectURL = vi.fn()

// Import APRES les stubs (la page resout les auto-imports au top-level du module).
const CarriersIndex = (await import('../carriers/index.vue')).default

// ── Stubs de composants ──────────────────────────────────────────────────────
const ButtonStub = defineComponent({
    props: { label: { type: String, default: '' }, disabled: { type: Boolean, default: false } },
    emits: ['click'],
    setup(props, { emit }) {
        return () => h('button', { 'data-label': props.label, onClick: () => emit('click') }, props.label)
    },
})

const DataTableStub = defineComponent({
    props: { items: { type: Array, default: () => [] } },
    emits: ['row-click', 'update:page', 'update:per-page'],
    setup(props, { emit }) {
        return () => h('div', { 'data-testid': 'datatable' },
            (props.items as Array<{ id: number }>).map(it =>
                h('tr', { 'data-row-id': it.id, onClick: () => emit('row-click', it) }),
            ),
        )
    },
})

const DrawerStub = defineComponent({
    props: { modelValue: { type: Boolean, default: false } },
    setup(_, { slots }) {
        return () => h('div', {}, [slots.header?.(), slots.default?.(), slots.footer?.()])
    },
})

const SlotStub = defineComponent({ setup(_, { slots }) { return () => h('div', {}, slots.default?.()) } })

const PageHeaderStub = defineComponent({
    setup(_, { slots }) { return () => h('div', {}, [slots.default?.(), slots.actions?.()]) },
})

const CheckboxStub = defineComponent({
    props: { id: { type: String, default: '' }, modelValue: { type: Boolean, default: false } },
    emits: ['update:model-value'],
    setup(props, { emit }) {
        return () => h('input', {
            'type': 'checkbox',
            'data-id': props.id,
            'onChange': (e: Event) => emit('update:model-value', (e.target as HTMLInputElement).checked),
        })
    },
})

const InputTextStub = defineComponent({ setup() { return () => h('input') } })

function mountPage() {
    return mount(CarriersIndex, {
        global: {
            stubs: {
                PageHeader: PageHeaderStub,
                MalioButton: ButtonStub,
                MalioDataTable: DataTableStub,
                MalioDrawer: DrawerStub,
                MalioAccordion: SlotStub,
                MalioAccordionItem: SlotStub,
                MalioInputText: InputTextStub,
                MalioCheckbox: CheckboxStub,
            },
        },
    })
}

describe('Répertoire transporteurs (page /carriers)', () => {
    beforeEach(() => {
        mockPush.mockReset()
        mockApiGet.mockReset().mockResolvedValue({ member: [] })
        mockCan.mockReset().mockReturnValue(true)
        mockSetFilters.mockReset()
        mockFetch.mockReset()
        mockToastError.mockReset()
    })

    it('charge la liste au montage', async () => {
        mountPage()
        await flushPromises()
        expect(mockFetch).toHaveBeenCalled()
    })

    it('affiche « + Ajouter » uniquement avec la permission manage', async () => {
        mockCan.mockImplementation((perm: string) => perm === 'transport.carriers.manage')
        const wrapper = mountPage()
        await flushPromises()
        expect(wrapper.find('[data-label="transport.carriers.add"]').exists()).toBe(true)
    })

    it('masque « + Ajouter » sans la permission manage (view seul)', async () => {
        mockCan.mockImplementation((perm: string) => perm === 'transport.carriers.view')
        const wrapper = mountPage()
        await flushPromises()
        expect(wrapper.find('[data-label="transport.carriers.add"]').exists()).toBe(false)
    })

    it('navigue vers la consultation au clic sur une ligne', async () => {
        const wrapper = mountPage()
        await flushPromises()
        await wrapper.find('tr[data-row-id="7"]').trigger('click')
        expect(mockPush).toHaveBeenCalledWith('/carriers/7')
    })

    it('appelle l\'export XLSX sur /carriers/export.xlsx en blob', async () => {
        const wrapper = mountPage()
        await flushPromises()
        await wrapper.find('[data-label="transport.carriers.export"]').trigger('click')
        await flushPromises()
        expect(mockApiGet).toHaveBeenCalledWith(
            '/carriers/export.xlsx',
            expect.any(Object),
            expect.objectContaining({ responseType: 'blob', toast: false }),
        )
    })

    it('repercute le filtre « Voir les archivés » dans setFilters sans toucher l\'URL', async () => {
        const wrapper = mountPage()
        await flushPromises()

        // Coche « Voir les archivés » puis applique les filtres.
        await wrapper.find('input[data-id="filter-archived-only"]').setValue(true)
        await wrapper.find('[data-label="transport.carriers.filters.apply"]').trigger('click')

        expect(mockSetFilters).toHaveBeenLastCalledWith(
            { archivedOnly: true },
            { replace: true },
        )
        // Etat 100 % local (regle n°6) : aucune navigation/query string declenchee.
        expect(mockPush).not.toHaveBeenCalled()
    })

    it('repercute les certifications cochees dans setFilters (filtre multi)', async () => {
        const wrapper = mountPage()
        await flushPromises()

        // Coche deux certifications via les cases a cocher (pattern repertoire clients).
        await wrapper.find('input[data-id="filter-certification-QUALIMAT"]').setValue(true)
        await wrapper.find('input[data-id="filter-certification-AUTRE"]').setValue(true)
        await wrapper.find('[data-label="transport.carriers.filters.apply"]').trigger('click')

        expect(mockSetFilters).toHaveBeenLastCalledWith(
            { 'certificationType[]': ['QUALIMAT', 'AUTRE'] },
            { replace: true },
        )
    })

    it('badge filtres actifs + Réinitialiser vide l\'etat applique', async () => {
        const wrapper = mountPage()
        await flushPromises()

        await wrapper.find('input[data-id="filter-archived-only"]').setValue(true)
        await wrapper.find('[data-label="transport.carriers.filters.apply"]').trigger('click')

        // Le libelle du bouton Filtrer porte le compteur (1 filtre actif).
        expect(wrapper.find('[data-label="transport.carriers.filters.title (1)"]').exists()).toBe(true)

        // Réinitialiser → query propre (setFilters avec objet vide).
        await wrapper.find('[data-label="transport.carriers.filters.reset"]').trigger('click')
        expect(mockSetFilters).toHaveBeenLastCalledWith({}, { replace: true })
    })
})
