<template>
    <div class="relative grid grid-cols-4 gap-x-[44px] gap-y-4 bg-white py-4 pl-[28px] pr-[60px] shadow-[0_4px_4px_0_rgba(0,0,0,0.25)]">
        <!-- Suppression : modal de confirmation cote parent. -->
        <MalioButtonIcon
            v-if="removable && !readonly && !disabled"
            icon="mdi:delete-outline"
            variant="ghost"
            button-class="absolute top-3 right-3"
            v-bind="{ ariaLabel: t('technique.providers.form.address.remove') }"
            @click="$emit('remove')"
        />

        <!-- Sites Starseed : multiselect a tags (>= 1 obligatoire, RG-3.05). -->
        <MalioSelectCheckbox
            v-if="!hideEmpty || isFilled(model.siteIris)"
            :model-value="model.siteIris"
            :options="siteOptions"
            :label="t('technique.providers.form.address.sites')"
            :display-tag="true"
            :readonly="readonly"
            :disabled="disabled"
            :required="!readonly && !disabled"
            :error="errors?.sites"
            @update:model-value="(v: (string | number)[]) => update('siteIris', v.map(String))"
        />

        <!-- Contacts rattaches (M2M, facultatif) : alimente par l'onglet Contact. -->
        <MalioSelectCheckbox
            v-if="!hideEmpty || isFilled(model.contactIris)"
            :model-value="model.contactIris"
            :options="contactOptions"
            :label="t('technique.providers.form.address.contacts')"
            :display-tag="true"
            :readonly="readonly"
            :disabled="disabled"
            @update:model-value="(v: (string | number)[]) => update('contactIris', v.map(String))"
        />

        <MalioSelect
            v-if="!hideEmpty || isFilled(model.country)"
            :model-value="model.country"
            :options="countryOptions"
            :label="t('technique.providers.form.address.country')"
            :readonly="readonly"
            :disabled="disabled"
            :required="!readonly && !disabled"
            @update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
        />

        <MalioInputText
            v-if="!hideEmpty || isFilled(model.postalCode)"
            :model-value="model.postalCode"
            :label="t('technique.providers.form.address.postalCode')"
            :mask="POSTAL_CODE_MASK"
            :readonly="readonly"
            :disabled="disabled"
            :required="!readonly && !disabled"
            :error="errors?.postalCode"
            @update:model-value="onPostalCodeChange"
        />

        <!-- Ville : MalioSelect alimente par le code postal (BAN). Saisie libre si BAN indispo. -->
        <MalioSelect
            v-if="!degraded && (!hideEmpty || isFilled(model.city))"
            :model-value="model.city"
            :options="cityOptions"
            :label="t('technique.providers.form.address.city')"
            :readonly="readonly"
            :disabled="disabled"
            empty-option-label=""
            :required="!readonly && !disabled"
            :error="errors?.city"
            @update:model-value="onCityChange"
        />
        <MalioInputText
            v-else-if="degraded && (!hideEmpty || isFilled(model.city))"
            :model-value="model.city"
            :label="t('technique.providers.form.address.city')"
            :mask="ADDRESS_MASK"
            :readonly="readonly"
            :disabled="disabled"
            :required="!readonly && !disabled"
            :error="errors?.city"
            @update:model-value="(v: string) => update('city', v)"
        />

        <!-- Adresse (BAN) sur 2 colonnes + Adresse complementaire. allow-create : le
             texte saisi est conserve si la BAN ne propose rien (saisie manuelle). -->
        <div v-if="!hideEmpty || isFilled(model.street)" class="col-span-2">
            <MalioInputAutocomplete
                v-if="!readonly && !disabled"
                :model-value="model.street"
                :options="addressOptions"
                :loading="addressLoading"
                :min-search-length="3"
                :label="t('technique.providers.form.address.street')"
                :readonly="readonly"
                :disabled="disabled"
                :required="!readonly && !disabled"
                :error="errors?.street"
                :allow-create="true"
                :no-results-text="t('technique.providers.form.address.streetNotFound')"
                @update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
                @search="onAddressSearch"
                @select="onAddressSelect"
            />
            <MalioInputText
                v-else
                :model-value="model.street"
                :label="t('technique.providers.form.address.street')"
                :readonly="readonly"
                :disabled="disabled"
                :required="!readonly && !disabled"
                :error="errors?.street"
                @update:model-value="(v: string) => update('street', v)"
            />
        </div>

        <div v-if="!hideEmpty || isFilled(model.streetComplement)" class="col-span-1">
            <MalioInputText
                :model-value="model.streetComplement"
                :label="t('technique.providers.form.address.streetComplement')"
                :mask="ADDRESS_MASK"
                :readonly="readonly"
                :disabled="disabled"
                :error="errors?.streetComplement"
                @update:model-value="(v: string) => update('streetComplement', v)"
            />
        </div>
    </div>
</template>

<script setup lang="ts">
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
import type { RefOption } from '~/modules/technique/composables/useProviderReferentials'
import type { ProviderAddressFormDraft } from '~/modules/technique/types/providerForm'
import { ADDRESS_MASK } from '~/shared/utils/textSanitize'
import { isFilled } from '~/shared/utils/consultationDisplay'

// Masque code postal FR : 5 chiffres.
const POSTAL_CODE_MASK = '#####'

const props = defineProps<{
    /** Brouillon de l'adresse (v-model). */
    modelValue: ProviderAddressFormDraft
    /** Sites Starseed disponibles. */
    siteOptions: RefOption[]
    /** Contacts deja saisis, rattachables a l'adresse. */
    contactOptions: RefOption[]
    /** Pays disponibles (France par defaut). */
    countryOptions: RefOption[]
    removable?: boolean
    readonly?: boolean
    /** Bloc desactive (champs grises, consultation — distinct de readonly). */
    disabled?: boolean
    /** Consultation : masque les champs non remplis (ERP-193). */
    hideEmpty?: boolean
    /** Erreurs serveur 422 de cette ligne, indexees par champ (ERP-101). */
    errors?: Record<string, string>
}>()

const emit = defineEmits<{
    'update:modelValue': [value: ProviderAddressFormDraft]
    'remove': []
    /** Emis une fois quand le service d'autocompletion bascule en indisponible. */
    'degraded': []
}>()

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

const model = computed(() => props.modelValue)

// Repli saisie libre de la VILLE quand la BAN est indisponible (recuperable).
const degraded = ref(false)
let unavailableNotified = false
const banCityOptions = ref<RefOption[]>([])
const banAddressOptions = ref<RefOption[]>([])

// Options ville effectives : on garantit que la ville courante figure toujours
// dans la liste, sinon MalioSelect afficherait un champ vide en lecture seule.
const cityOptions = computed<RefOption[]>(() => {
    const current = props.modelValue.city
    if (current && !banCityOptions.value.some(o => o.value === current)) {
        return [{ value: current, label: current }, ...banCityOptions.value]
    }
    return banCityOptions.value
})

// Meme garantie pour le champ Adresse : la rue courante doit toujours figurer
// dans les options, sinon MalioInputAutocomplete laisse le champ vide.
const addressOptions = computed<RefOption[]>(() => {
    const current = props.modelValue.street
    if (current && !banAddressOptions.value.some(o => o.value === current)) {
        return [{ value: current, label: current }, ...banAddressOptions.value]
    }
    return banAddressOptions.value
})
const addressLoading = ref(false)
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
let lastAddressSuggestions: AddressSuggestion[] = []

// Filtrage des caracteres parasites : porte par le mask ADDRESS_MASK (maska) sur
// les champs texte editables (complement, ville en mode degrade). La voie en
// autocomplete (BAN) et la ville en select ne sont pas masquees (le back valide
// via Assert\Regex).

/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
function update<K extends keyof ProviderAddressFormDraft>(field: K, value: ProviderAddressFormDraft[K]): void {
    emit('update:modelValue', { ...props.modelValue, [field]: value })
}

/**
 * Selection d'une ville (select assiste BAN) → vide adresse + complement, devenus
 * incoherents avec la nouvelle ville. Ne reagit qu'a un vrai changement de valeur.
 * En mode degrade (saisie libre), la ville reste un simple `update` (pas de reset
 * a chaque frappe).
 */
function onCityChange(value: string | number | null): void {
    const next = value === null ? null : String(value)
    if (next === (props.modelValue.city ?? null)) {
        return
    }
    banAddressOptions.value = []
    lastAddressSuggestions = []
    emit('update:modelValue', {
        ...props.modelValue,
        city: next,
        street: null,
        streetComplement: null,
    })
}

/** Previent le parent (toast unique) que l'autocompletion est indisponible. */
function notifyUnavailable(): void {
    if (!unavailableNotified) {
        unavailableNotified = true
        emit('degraded')
    }
}

/** Saisie du code postal → met a jour le champ + interroge la BAN pour la ville (RG-3.06). */
async function onPostalCodeChange(value: string): Promise<void> {
    const digits = (value ?? '').replace(/\D/g, '')
    const previousDigits = (props.modelValue.postalCode ?? '').replace(/\D/g, '')

    // CP complet (5 chiffres) et reellement modifie → ville, adresse et complement
    // deviennent incoherents avec le nouveau code postal : on les vide pour forcer
    // une re-saisie coherente (on n'efface pas pendant une correction partielle).
    if (digits.length === 5 && digits !== previousDigits) {
        banAddressOptions.value = []
        lastAddressSuggestions = []
        emit('update:modelValue', {
            ...props.modelValue,
            postalCode: value,
            city: null,
            street: null,
            streetComplement: null,
        })
    }
    else {
        update('postalCode', value)
    }

    if (digits.length < 5) {
        return
    }
    try {
        const suggestions = await autocomplete.searchCity(digits)
        banCityOptions.value = suggestions.map(s => ({ value: s.city, label: s.city }))
        degraded.value = false
    }
    catch {
        degraded.value = true
        notifyUnavailable()
    }
}

/** Recherche d'adresse assistee (event de MalioInputAutocomplete). */
async function onAddressSearch(query: string): Promise<void> {
    // La BAN exige au moins 3 caracteres : on n'envoie rien en deca (evite un 400).
    if (query.trim().length < 3) {
        banAddressOptions.value = []
        return
    }
    addressLoading.value = true
    try {
        const postalCode = (model.value.postalCode ?? '').replace(/\D/g, '') || undefined
        const suggestions = await autocomplete.searchAddress(query, postalCode)
        lastAddressSuggestions = suggestions
        banAddressOptions.value = suggestions.map(s => ({ value: s.street, label: s.label }))
    }
    catch {
        // Erreur transitoire : on vide les suggestions, la prochaine frappe reessaie.
        banAddressOptions.value = []
        notifyUnavailable()
    }
    finally {
        addressLoading.value = false
    }
}

/** Selection d'une suggestion d'adresse → remplit rue + ville + CP. */
function onAddressSelect(option: { label: string, value: string | number } | null): void {
    if (option === null) {
        return
    }
    const suggestion = lastAddressSuggestions.find(s => s.street === option.value)
    if (!suggestion) {
        update('street', String(option.value))
        return
    }
    emit('update:modelValue', {
        ...props.modelValue,
        street: suggestion.street,
        city: suggestion.city,
        postalCode: suggestion.postalCode,
    })
}
</script>
