import { beforeEach, describe, expect, it, vi } from 'vitest'

/**
 * Tests du workflow « Ajouter un transporteur » (M4 Transport, ERP-165).
 *
 * `useCarrierForm` porte le formulaire principal (Nom + Certification + Affréter)
 * et l'orchestration des onglets de création. On vérifie ici le CONTRAT propre à
 * la création :
 *  - pré-check front : nom requis → POST bloqué, erreur inline, aucun appel réseau ;
 *  - POST /carriers (groupe carrier:write:main) : payload + Accept ld+json +
 *    toast:false ; au succès, verrouillage + bascule sur l'onglet Qualimat +
 *    réaffichage du nom normalisé ;
 *  - 409 doublon (RG-4.12) → erreur inline dédiée sur `name` ;
 *  - 422 → mapping inline par champ (propertyPath) ;
 *  - onglets : 4 onglets (Qualimat/Adresses/Contacts/Prix), completeTab
 *    déverrouille/avance et signale le dernier onglet ;
 *  - patchCarrier : PATCH partiel, no-op avant création.
 */

const mockPost = vi.hoisted(() => vi.fn())
const mockPatch = vi.hoisted(() => vi.fn())

vi.stubGlobal('useApi', () => ({
    get: vi.fn(),
    post: mockPost,
    put: vi.fn(),
    patch: mockPatch,
    delete: vi.fn(),
}))
vi.stubGlobal('useI18n', () => ({ t: (key: string) => key }))
vi.stubGlobal('useToast', () => ({
    success: vi.fn(),
    error: vi.fn(),
    warning: vi.fn(),
    info: vi.fn(),
}))

const { useCarrierForm, CARRIER_TAB_KEYS } = await import('../useCarrierForm')

describe('useCarrierForm', () => {
    beforeEach(() => {
        mockPost.mockReset()
        mockPatch.mockReset()
    })

    it('front : nom vide → erreur inline sur name, pas de POST', async () => {
        const form = useCarrierForm()

        const created = await form.submitMain()

        expect(created).toBe(false)
        expect(mockPost).not.toHaveBeenCalled()
        expect(form.mainErrors.errors.name).toBe('transport.carriers.form.errors.nameRequired')
        expect(form.mainLocked.value).toBe(false)
    })

    it('front : nom en espaces uniquement → erreur inline, pas de POST', async () => {
        const form = useCarrierForm()
        form.main.name = '   '

        const created = await form.submitMain()

        expect(created).toBe(false)
        expect(mockPost).not.toHaveBeenCalled()
        expect(form.mainErrors.errors.name).toBe('transport.carriers.form.errors.nameRequired')
    })

    it('POST /carriers avec Accept ld+json, verrouille et bascule sur Qualimat', async () => {
        mockPost.mockResolvedValueOnce({ id: 42, name: 'TRANSPORTS ACME', certificationType: 'GMP_PLUS' })
        const form = useCarrierForm()
        form.main.name = 'Transports Acme'
        form.main.certificationType = 'GMP_PLUS'
        form.main.isChartered = true

        const created = await form.submitMain()

        expect(created).toBe(true)
        expect(mockPost).toHaveBeenCalledTimes(1)
        const [url, body, opts] = mockPost.mock.calls[0] ?? []
        expect(url).toBe('/carriers')
        expect(body).toEqual({
            name: 'Transports Acme',
            certificationType: 'GMP_PLUS',
            isChartered: true,
        })
        expect(opts).toMatchObject({ toast: false, headers: { Accept: 'application/ld+json' } })

        expect(form.carrierId.value).toBe(42)
        // RG-4.13 : réaffiche le nom normalisé (UPPERCASE) renvoyé par le serveur.
        expect(form.main.name).toBe('TRANSPORTS ACME')
        expect(form.mainLocked.value).toBe(true)
        expect(form.activeTab.value).toBe('qualimat')
        expect(form.unlockedIndex.value).toBe(0)
    })

    it('payload : omet name et certificationType vides, garde isChartered', async () => {
        mockPost.mockRejectedValueOnce({ response: { status: 422, _data: { violations: [] } } })
        const form = useCarrierForm()
        form.main.name = 'X' // nom présent pour passer le pré-check front
        // certificationType laissé null → omis pour que la 422 « obligatoire » porte.

        await form.submitMain()

        const body = mockPost.mock.calls[0]?.[1] as Record<string, unknown>
        expect(body).toEqual({ name: 'X', isChartered: false })
        expect('certificationType' in body).toBe(false)
    })

    it('409 doublon (RG-4.12) : erreur inline dédiée sur name, pas de verrouillage', async () => {
        mockPost.mockRejectedValueOnce({ response: { status: 409 } })
        const form = useCarrierForm()
        form.main.name = 'Doublon'
        form.main.certificationType = 'AUTRE'

        const created = await form.submitMain()

        expect(created).toBe(false)
        expect(form.mainErrors.errors.name).toBe('transport.carriers.form.duplicateName')
        expect(form.mainLocked.value).toBe(false)
    })

    it('422 : mappe les violations serveur inline par champ', async () => {
        mockPost.mockRejectedValueOnce({
            response: {
                status: 422,
                _data: { violations: [{ propertyPath: 'certificationType', message: 'Le type de certification est obligatoire.' }] },
            },
        })
        const form = useCarrierForm()
        form.main.name = 'Sans Certif'

        const created = await form.submitMain()

        expect(created).toBe(false)
        expect(form.mainErrors.errors.certificationType).toBe('Le type de certification est obligatoire.')
        expect(form.mainLocked.value).toBe(false)
    })

    it('onglets : 4 clés Qualimat/Adresses/Contacts/Prix', () => {
        expect(CARRIER_TAB_KEYS).toEqual(['qualimat', 'addresses', 'contacts', 'prices'])
        const form = useCarrierForm()
        expect(form.tabKeys.value).toEqual(['qualimat', 'addresses', 'contacts', 'prices'])
        // Tous verrouillés tant que le formulaire principal n'est pas validé.
        expect(form.unlockedIndex.value).toBe(-1)
    })

    it('completeTab : déverrouille/avance, et signale le dernier onglet du flux', () => {
        const form = useCarrierForm()

        // Qualimat → Adresses (pas le dernier).
        expect(form.completeTab('qualimat')).toBe(false)
        expect(form.isValidated('qualimat')).toBe(true)
        expect(form.activeTab.value).toBe('addresses')
        expect(form.unlockedIndex.value).toBe(1)

        expect(form.completeTab('addresses')).toBe(false)
        expect(form.activeTab.value).toBe('contacts')

        expect(form.completeTab('contacts')).toBe(false)
        expect(form.activeTab.value).toBe('prices')

        // Prix = dernier onglet → true (création terminée).
        expect(form.completeTab('prices')).toBe(true)
        expect(form.isValidated('prices')).toBe(true)
    })

    it('editMode : completeTab ne verrouille pas et ne bascule pas d\'onglet', () => {
        const form = useCarrierForm()
        form.editMode.value = true
        form.activeTab.value = 'qualimat'

        expect(form.completeTab('qualimat')).toBe(false)
        expect(form.isValidated('qualimat')).toBe(false)
        expect(form.activeTab.value).toBe('qualimat')
    })

    it('patchCarrier : PATCH /carriers/{id} en mode strict, no-op avant création', async () => {
        const form = useCarrierForm()

        await form.patchCarrier({ liotPlates: 'AA-123-BB' })
        expect(mockPatch).not.toHaveBeenCalled()

        mockPost.mockResolvedValueOnce({ id: 9, name: 'ACME', certificationType: 'OVOCOM' })
        form.main.name = 'Acme'
        form.main.certificationType = 'OVOCOM'
        await form.submitMain()

        await form.patchCarrier({ liotPlates: 'AA-123-BB' })
        expect(mockPatch).toHaveBeenCalledWith('/carriers/9', { liotPlates: 'AA-123-BB' }, { toast: false })
    })
})

describe('useCarrierForm — champs conditionnels (ERP-166)', () => {
    beforeEach(() => {
        mockPost.mockReset()
        mockPatch.mockReset()
    })

    it('cas LIOT (insensible à la casse) : masque la certification, payload réduit', () => {
        const form = useCarrierForm()
        form.main.name = 'liot'

        expect(form.isLiot.value).toBe(true)
        expect(form.showCertification.value).toBe(false)

        form.main.liotPlates = 'AA-123-BB ; CC-456-DD'
        expect(form.buildMainPayload()).toEqual({
            name: 'liot',
            isChartered: false,
            liotPlates: 'AA-123-BB ; CC-456-DD',
        })
    })

    it('LIOT masque les champs conditionnels (affrètement / décharge)', () => {
        const form = useCarrierForm()
        form.main.name = 'LIOT'
        form.main.isChartered = true
        form.main.certificationType = 'AUTRE'

        expect(form.showCharteredFields.value).toBe(false)
        expect(form.showDischarge.value).toBe(false)
    })

    it('RG-4.03 affrété : indexation / contenant / volume visibles et dans le payload', () => {
        const form = useCarrierForm()
        form.main.name = 'Acme'
        form.main.certificationType = 'GMP_PLUS'
        form.main.isChartered = true

        expect(form.showCharteredFields.value).toBe(true)

        form.main.indexationRate = '5'
        form.main.containerType = 'BENNE'
        form.main.volumeM3 = '30'

        expect(form.buildMainPayload()).toEqual({
            name: 'Acme',
            certificationType: 'GMP_PLUS',
            isChartered: true,
            indexationRate: '5',
            containerType: 'BENNE',
            volumeM3: '30',
        })
    })

    it('RG-4.03 affrété mais champs vides : omis du payload (422 NotBlank back)', () => {
        const form = useCarrierForm()
        form.main.name = 'Acme'
        form.main.certificationType = 'GMP_PLUS'
        form.main.isChartered = true

        expect(form.buildMainPayload()).toEqual({
            name: 'Acme',
            certificationType: 'GMP_PLUS',
            isChartered: true,
        })
    })

    it('RG-4.02 AUTRE : décharge visible + dischargeDocument dans le payload si IRI résolu', () => {
        const form = useCarrierForm()
        form.main.name = 'Acme'
        form.main.certificationType = 'AUTRE'

        expect(form.showDischarge.value).toBe(true)

        form.main.dischargeDocumentIri = '/api/uploaded_documents/7'
        expect(form.buildMainPayload()).toMatchObject({ dischargeDocument: '/api/uploaded_documents/7' })
    })
})

describe('useCarrierForm — copie QUALIMAT (ERP-166)', () => {
    const QUALIMAT_ROW = {
        '@id': '/api/qualimat_carriers/42',
        id: '42',
        name: 'TRANSPORTS QUALIMAT',
        siret: '12345678900012',
        address: '1 rue du Port',
        postalCode: '86000',
        city: 'Poitiers',
        validityDate: '2027-01-15',
        status: 'VALIDE',
    }

    beforeEach(() => {
        mockPost.mockReset()
        mockPatch.mockReset()
    })

    it('copie name + certificationType=QUALIMAT (readonly) + IRI + adresse, sans PATCH avant création', async () => {
        const form = useCarrierForm()

        const ok = await form.applyQualimatSelection(QUALIMAT_ROW)

        expect(ok).toBe(true)
        expect(mockPatch).not.toHaveBeenCalled()
        expect(form.main.name).toBe('TRANSPORTS QUALIMAT')
        expect(form.main.certificationType).toBe('QUALIMAT')
        expect(form.main.qualimatCarrierIri).toBe('/api/qualimat_carriers/42')
        expect(form.isQualimat.value).toBe(true)
        expect(form.certificationReadonly.value).toBe(true)
        expect(form.qualimatAddress.value).toEqual({
            country: 'France',
            postalCode: '86000',
            city: 'Poitiers',
            street: '1 rue du Port',
        })
    })

    it('après création : PATCH /carriers/{id} avec qualimatCarrier + name + certification', async () => {
        mockPost.mockResolvedValueOnce({ id: 9, name: 'X', certificationType: 'GMP_PLUS' })
        mockPatch.mockResolvedValueOnce({})
        const form = useCarrierForm()
        form.main.name = 'X'
        form.main.certificationType = 'GMP_PLUS'
        await form.submitMain()

        const ok = await form.applyQualimatSelection(QUALIMAT_ROW)

        expect(ok).toBe(true)
        expect(mockPatch).toHaveBeenCalledWith(
            '/carriers/9',
            {
                qualimatCarrier: '/api/qualimat_carriers/42',
                name: 'TRANSPORTS QUALIMAT',
                certificationType: 'QUALIMAT',
            },
            { toast: false },
        )
    })

    it('buildMainPayload inclut qualimatCarrier + certificationType QUALIMAT après intégration', async () => {
        const form = useCarrierForm()
        form.main.name = 'Acme'
        await form.applyQualimatSelection(QUALIMAT_ROW)

        expect(form.buildMainPayload()).toMatchObject({
            qualimatCarrier: '/api/qualimat_carriers/42',
            certificationType: 'QUALIMAT',
        })
    })
})
