<template>
    <!-- Bloc à plat (sans box-shadow) : un filet noir 1px le sépare du suivant
         (pas de bordure sous le dernier bloc), comme sur Starseed. -->
    <div class="pb-5" :class="{ 'border-b border-black': !last }">
        <div class="flex items-center justify-between">
            <h3 class="text-[20px] font-semibold text-black">{{ title }}</h3>
            <MalioButtonIcon
                v-if="removable && !readonly"
                icon="mdi:delete-outline"
                variant="ghost"
                button-class="p-0"
                :aria-label="$t('common.delete')"
                @click="$emit('remove')"
            />
        </div>

        <div class="mt-6 grid grid-cols-2 gap-x-[44px] gap-y-4">
        <MalioInputText
            class="col-span-2"
            :label="$t('directory.addresses.fields.label')"
            :model-value="modelValue.label ?? ''"
            :readonly="readonly"
            @update:model-value="update('label', $event)"
        />

        <!-- Rue : saisie assistée (BAN) en édition, champ texte en lecture seule.
             allow-create conserve le texte saisi si la BAN ne propose rien
             (erreur/timeout). Choisir une suggestion remplit rue + CP + ville. -->
        <div class="col-span-2">
            <MalioInputAutocomplete
                v-if="!readonly"
                :model-value="modelValue.street ?? ''"
                :options="addressOptions"
                :loading="addressLoading"
                :min-search-length="3"
                :allow-create="true"
                :label="$t('directory.addresses.fields.street')"
                :no-results-text="$t('directory.addresses.streetNotFound')"
                @update:model-value="(v) => update('street', v === null ? '' : String(v))"
                @search="onAddressSearch"
                @select="onAddressSelect"
            />
            <MalioInputText
                v-else
                :label="$t('directory.addresses.fields.street')"
                :model-value="modelValue.street ?? ''"
                :readonly="readonly"
                @update:model-value="update('street', $event)"
            />
        </div>

        <MalioInputText
            class="col-span-2"
            :label="$t('directory.addresses.fields.streetComplement')"
            :model-value="modelValue.streetComplement ?? ''"
            :readonly="readonly"
            @update:model-value="update('streetComplement', $event)"
        />

        <MalioInputText
            :label="$t('directory.addresses.fields.postalCode')"
            :model-value="modelValue.postalCode ?? ''"
            :readonly="readonly"
            @update:model-value="onPostalCodeInput"
        />

        <!-- Ville : select alimenté par le code postal (BAN). En mode dégradé
             (BAN indispo) ou lecture seule, on bascule en saisie libre. -->
        <MalioSelect
            v-if="!readonly && !degraded"
            :model-value="modelValue.city ?? ''"
            :options="cityOptions"
            :label="$t('directory.addresses.fields.city')"
            empty-option-label=""
            group-class="w-full"
            @update:model-value="(v) => update('city', v === null ? '' : String(v))"
        />
        <MalioInputText
            v-else
            :label="$t('directory.addresses.fields.city')"
            :model-value="modelValue.city ?? ''"
            :readonly="readonly"
            @update:model-value="update('city', $event)"
        />
        </div>
    </div>
</template>

<script setup lang="ts">
import type { Address } from '~/modules/directory/services/dto/address'
import {
    useAddressAutocomplete,
    type AddressSuggestion,
} from '~/modules/directory/composables/useAddressAutocomplete'

const props = defineProps<{
    modelValue: Address
    title: string
    removable?: boolean
    readonly?: boolean
    /** Dernier bloc de la liste : supprime le filet de séparation bas. */
    last?: boolean
}>()

const emit = defineEmits<{
    'update:modelValue': [value: Address]
    'remove': []
}>()

const { t } = useI18n()
const toast = useToast()
const autocomplete = useAddressAutocomplete()

type Option = { label: string, value: string | number }

const addressOptions = ref<Option[]>([])
// Villes renvoyées par la BAN pour le code postal courant.
const fetchedCityOptions = ref<Option[]>([])
const addressLoading = ref(false)

// Le select Ville n'affiche que les valeurs présentes dans ses options : on
// garantit donc que la ville déjà enregistrée (chargement d'une fiche) ou
// pré-remplie par l'autocomplétion d'adresse figure toujours dans la liste,
// même avant toute recherche par code postal — sinon elle s'afficherait vide.
const cityOptions = computed<Option[]>(() => {
    const current = (props.modelValue.city ?? '').trim()
    const options = [...fetchedCityOptions.value]
    if (current && !options.some(o => o.value === current)) {
        options.unshift({ value: current, label: current })
    }
    return options
})
// Mode dégradé : BAN indisponible → la ville passe en saisie libre.
const degraded = ref(false)
let lastAddressSuggestions: AddressSuggestion[] = []
let notified = false

function update(field: keyof Address, value: string): void {
    emit('update:modelValue', { ...props.modelValue, [field]: value === '' ? null : value })
}

// Avertit une seule fois que l'autocomplétion est indisponible (saisie libre).
function notifyUnavailable(): void {
    if (notified) return
    notified = true
    toast.info({ title: '', message: t('directory.addresses.autocompleteUnavailable') })
}

/** Recherche d'adresse assistée (event de MalioInputAutocomplete). */
async function onAddressSearch(query: string): Promise<void> {
    if (query.trim().length < 3) {
        addressOptions.value = []
        return
    }
    addressLoading.value = true
    try {
        const postalCode = (props.modelValue.postalCode ?? '').replace(/\D/g, '') || undefined
        const suggestions = await autocomplete.searchAddress(query, postalCode)
        lastAddressSuggestions = suggestions
        addressOptions.value = suggestions.map(s => ({ value: s.street, label: s.label }))
    }
    catch {
        addressOptions.value = []
        notifyUnavailable()
    }
    finally {
        addressLoading.value = false
    }
}

/** Sélection d'une suggestion → remplit rue + ville + code postal. */
function onAddressSelect(option: Option | null): void {
    if (option === null) return
    // Matching par `label` (adresse complète, unique côté BAN) plutôt que par
    // rue : deux communes peuvent partager le même libellé de voie.
    const suggestion = lastAddressSuggestions.find(s => s.label === option.label)
    if (!suggestion) {
        update('street', String(option.value))
        return
    }
    emit('update:modelValue', {
        ...props.modelValue,
        street: suggestion.street,
        city: suggestion.city || props.modelValue.city,
        postalCode: suggestion.postalCode || props.modelValue.postalCode,
    })
}

/** Saisie du code postal → met à jour le champ + interroge la BAN pour la ville. */
async function onPostalCodeInput(value: string): Promise<void> {
    update('postalCode', value)
    const digits = (value ?? '').replace(/\D/g, '')
    if (digits.length < 5) return
    try {
        const suggestions = await autocomplete.searchCity(digits)
        fetchedCityOptions.value = suggestions.map(s => ({ value: s.city, label: s.city }))
        degraded.value = false
    }
    catch {
        degraded.value = true
        notifyUnavailable()
    }
}
</script>
