import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { defineComponent, h, ref, computed } from 'vue'
import { emptyCarrierAddress } from '~/modules/transport/types/carrierForm'
import CarrierAddressBlock from '../CarrierAddressBlock.vue'

/**
 * Tests de l'autocomplétion BAN du bloc Adresse transporteur (ERP-167) — réutilise
 * `useAddressAutocomplete` (M1/M2/M3). On vérifie le NOMINAL (CP → ville) et le
 * DÉGRADÉ (BAN indisponible → saisie libre + event `degraded`).
 */

const { searchCityMock, searchAddressMock } = vi.hoisted(() => ({
    searchCityMock: vi.fn(),
    searchAddressMock: vi.fn(),
}))
vi.mock('~/shared/composables/useAddressAutocomplete', () => ({
    useAddressAutocomplete: () => ({
        searchCity: searchCityMock,
        searchAddress: searchAddressMock,
    }),
}))

vi.stubGlobal('useI18n', () => ({ t: (key: string) => key }))
vi.stubGlobal('ref', ref)
vi.stubGlobal('computed', computed)

const MalioInputTextStub = defineComponent({
    name: 'MalioInputText',
    props: { modelValue: { default: null }, label: { type: String, default: '' }, error: { type: String, default: '' } },
    emits: ['update:modelValue'],
    setup(props) {
        return () => h('div', { 'data-testid': 'input-text', 'data-label': props.label, 'data-error': props.error })
    },
})

const MalioSelectStub = defineComponent({
    name: 'MalioSelect',
    props: { modelValue: { default: null }, options: { type: Array as () => { value: string }[], default: () => [] }, label: { type: String, default: '' }, error: { type: String, default: '' } },
    emits: ['update:modelValue'],
    setup(props) {
        return () => h('div', { 'data-testid': 'select', 'data-label': props.label, 'data-options': JSON.stringify(props.options.map(o => o.value)) })
    },
})

const MalioInputAutocompleteStub = defineComponent({
    name: 'MalioInputAutocomplete',
    props: { modelValue: { default: null }, options: { type: Array as () => { value: string }[], default: () => [] }, loading: { type: Boolean, default: false }, allowCreate: { type: Boolean, default: false } },
    emits: ['update:modelValue', 'search', 'select'],
    setup(props) {
        return () => h('div', { 'data-testid': 'addr-autocomplete', 'data-options': JSON.stringify(props.options.map(o => o.value)) })
    },
})

function mountBlock(overrides: Record<string, unknown> = {}) {
    return mount(CarrierAddressBlock, {
        props: {
            modelValue: { ...emptyCarrierAddress(), ...overrides },
            countryOptions: [{ value: 'France', label: 'France' }],
        },
        global: {
            stubs: {
                MalioButtonIcon: true,
                MalioInputText: MalioInputTextStub,
                MalioSelect: MalioSelectStub,
                MalioInputAutocomplete: MalioInputAutocompleteStub,
            },
        },
    })
}

/** Récupère le composant MalioInputText d'un label donné. */
function inputTextByLabel(wrapper: ReturnType<typeof mountBlock>, label: string) {
    return wrapper.findAllComponents(MalioInputTextStub).find(c => c.props('label') === label)
}

describe('CarrierAddressBlock — autocomplétion ville (BAN) NOMINAL', () => {
    beforeEach(() => {
        searchCityMock.mockReset()
        searchAddressMock.mockReset()
    })

    it('saisie d\'un CP à 5 chiffres → searchCity + peuple le select Ville', async () => {
        searchCityMock.mockResolvedValueOnce([{ city: 'Poitiers', postalCode: '86000' }])
        const wrapper = mountBlock()

        const cp = inputTextByLabel(wrapper, 'transport.carriers.form.address.postalCode')
        cp?.vm.$emit('update:modelValue', '86000')
        await flushPromises()

        expect(searchCityMock).toHaveBeenCalledWith('86000')
        const citySelect = wrapper.findAllComponents(MalioSelectStub).find(c => c.props('label') === 'transport.carriers.form.address.city')
        const options = JSON.parse(citySelect?.attributes('data-options') ?? '[]')
        expect(options).toContain('Poitiers')
        expect(wrapper.emitted('degraded')).toBeUndefined()
    })

    it('n\'interroge pas la BAN sous 5 chiffres', async () => {
        const wrapper = mountBlock()
        inputTextByLabel(wrapper, 'transport.carriers.form.address.postalCode')?.vm.$emit('update:modelValue', '860')
        await flushPromises()
        expect(searchCityMock).not.toHaveBeenCalled()
    })
})

describe('CarrierAddressBlock — autocomplétion DÉGRADÉE', () => {
    beforeEach(() => {
        searchCityMock.mockReset()
        searchAddressMock.mockReset()
    })

    it('BAN ville indisponible → bascule en saisie libre + émet « degraded »', async () => {
        searchCityMock.mockRejectedValueOnce(new Error('BAN indisponible'))
        const wrapper = mountBlock()

        inputTextByLabel(wrapper, 'transport.carriers.form.address.postalCode')?.vm.$emit('update:modelValue', '86000')
        await flushPromises()

        expect(wrapper.emitted('degraded')).toHaveLength(1)
        // En dégradé, la Ville devient un MalioInputText (plus de MalioSelect ville).
        const citySelect = wrapper.findAllComponents(MalioSelectStub).find(c => c.props('label') === 'transport.carriers.form.address.city')
        expect(citySelect).toBeUndefined()
        expect(inputTextByLabel(wrapper, 'transport.carriers.form.address.city')).toBeDefined()
    })

    it('autocomplétion adresse : pas d\'appel BAN sous 3 caractères', async () => {
        const wrapper = mountBlock()
        wrapper.findComponent(MalioInputAutocompleteStub).vm.$emit('search', 'ab')
        await flushPromises()
        expect(searchAddressMock).not.toHaveBeenCalled()
    })

    it('autocomplétion adresse : émet « degraded » une seule fois malgré plusieurs erreurs', async () => {
        searchAddressMock.mockRejectedValue(new Error('BAN indisponible'))
        const wrapper = mountBlock()
        const auto = wrapper.findComponent(MalioInputAutocompleteStub)

        auto.vm.$emit('search', 'rue de la paix')
        await flushPromises()
        auto.vm.$emit('search', 'rue de la paixx')
        await flushPromises()

        expect(wrapper.emitted('degraded')).toHaveLength(1)
    })

    it('active allow-create sur le champ Adresse (saisie manuelle libre)', () => {
        const wrapper = mountBlock()
        expect(wrapper.findComponent(MalioInputAutocompleteStub).props('allowCreate')).toBe(true)
    })
})
