import { describe, it, expect, vi, beforeEach } from 'vitest'
import {
    useAddressAutocomplete,
    AddressAutocompleteUnavailableError,
} from '../useAddressAutocomplete'

// On mocke le helper d'appel externe : aucun vrai appel reseau a la BAN.
// vi.mock est hoiste par Vitest au-dessus des imports.
const mockHttp = vi.hoisted(() => vi.fn())
vi.mock('~/shared/utils/httpExternal', () => ({ httpExternal: mockHttp }))

const BAN_URL = 'https://api-adresse.data.gouv.fr/search/'

describe('useAddressAutocomplete', () => {
    beforeEach(() => {
        mockHttp.mockReset()
    })

    describe('searchCity', () => {
        it('interroge la BAN en type=municipality et mappe { city, postalCode }', async () => {
            mockHttp.mockResolvedValueOnce({
                type: 'FeatureCollection',
                features: [
                    { properties: { city: 'Amiens', postcode: '80000', name: 'Amiens', type: 'municipality' } },
                    { properties: { city: 'Amiens', postcode: '80080', name: 'Amiens', type: 'municipality' } },
                ],
            })

            const { searchCity } = useAddressAutocomplete()
            const res = await searchCity('80000')

            expect(mockHttp).toHaveBeenCalledWith(
                BAN_URL,
                expect.objectContaining({ query: { q: '80000', type: 'municipality' } }),
            )
            expect(res).toEqual([
                { city: 'Amiens', postalCode: '80000' },
                { city: 'Amiens', postalCode: '80080' },
            ])
        })

        it('throw une AddressAutocompleteUnavailableError sur erreur reseau / 5xx', async () => {
            mockHttp.mockRejectedValueOnce(new Error('500 Server Error'))

            const { searchCity } = useAddressAutocomplete()

            await expect(searchCity('80000')).rejects.toBeInstanceOf(AddressAutocompleteUnavailableError)
        })

        it('throw une AddressAutocompleteUnavailableError sur timeout', async () => {
            mockHttp.mockRejectedValueOnce(new Error('The operation was aborted due to timeout'))

            const { searchCity } = useAddressAutocomplete()

            await expect(searchCity('80000')).rejects.toBeInstanceOf(AddressAutocompleteUnavailableError)
        })
    })

    describe('searchAddress', () => {
        it('interroge la BAN avec postcode et mappe la suggestion', async () => {
            mockHttp.mockResolvedValueOnce({
                type: 'FeatureCollection',
                features: [
                    {
                        properties: {
                            label: '8 Boulevard du Port 80000 Amiens',
                            name: '8 Boulevard du Port',
                            street: 'Boulevard du Port',
                            postcode: '80000',
                            city: 'Amiens',
                            type: 'housenumber',
                        },
                    },
                ],
            })

            const { searchAddress } = useAddressAutocomplete()
            const res = await searchAddress('8 boulevard du port', '80000')

            expect(mockHttp).toHaveBeenCalledWith(
                BAN_URL,
                expect.objectContaining({
                    query: { q: '8 boulevard du port', postcode: '80000' },
                }),
            )
            expect(res).toEqual([
                {
                    label: '8 Boulevard du Port 80000 Amiens',
                    street: '8 Boulevard du Port',
                    postalCode: '80000',
                    city: 'Amiens',
                },
            ])
        })

        it('omet le parametre postcode quand aucun code postal n\'est fourni', async () => {
            mockHttp.mockResolvedValueOnce({ type: 'FeatureCollection', features: [] })

            const { searchAddress } = useAddressAutocomplete()
            await searchAddress('8 boulevard du port')

            expect(mockHttp).toHaveBeenCalledWith(
                BAN_URL,
                expect.objectContaining({
                    query: { q: '8 boulevard du port' },
                }),
            )
        })

        it('ne restreint PAS la recherche a type=housenumber (sinon la BAN ne renvoie rien tant qu\'aucun numero n\'est saisi)', async () => {
            // Regression : avec `type=housenumber`, une saisie de nom de rue sans
            // numero (ex: « boulevard du port ») renvoie 0 resultat cote BAN.
            mockHttp.mockResolvedValueOnce({ type: 'FeatureCollection', features: [] })

            const { searchAddress } = useAddressAutocomplete()
            await searchAddress('boulevard du port', '80000')

            const sentQuery = mockHttp.mock.calls[0]?.[1]?.query as Record<string, string>
            expect(sentQuery.type).toBeUndefined()
        })

        it('throw une AddressAutocompleteUnavailableError sur erreur reseau', async () => {
            mockHttp.mockRejectedValueOnce(new Error('network down'))

            const { searchAddress } = useAddressAutocomplete()

            await expect(searchAddress('8 boulevard du port', '80000')).rejects.toBeInstanceOf(
                AddressAutocompleteUnavailableError,
            )
        })
    })
})
