<template>
    <div data-testid="geo-pin">
        <div class="mb-1 flex items-center gap-2">
            <span class="text-sm font-medium text-gray-700">{{ t('commercial.geo.title') }}</span>
            <!-- Badge « a geolocaliser » : adresse valide mais sans coordonnees
                 (spec M6 § 3.2 — exclue du calcul de tournee, RG-6.05). -->
            <span
                v-if="!hasCoords"
                class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800"
                data-testid="geo-badge-missing"
            >
                {{ t('commercial.geo.toGeolocate') }}
            </span>
            <!-- Pin fige a la main (RG-6.08) : informatif. -->
            <span
                v-else-if="geoManual"
                class="inline-flex items-center rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-800"
                data-testid="geo-badge-manual"
            >
                {{ t('commercial.geo.manualPin') }}
            </span>
        </div>

        <!-- Mini-carte Leaflet (exception documentee a @malio/layer-ui : carte
             interactive, type non couvert par la lib — cf. frontend.md
             § Composants formulaires). TODO : migrer si la lib couvre un jour
             les cartes. -->
        <div
            v-if="hasCoords"
            ref="mapEl"
            class="h-48 w-full rounded border border-gray-200"
            data-testid="geo-map"
        />
        <p v-if="hasCoords && !readonly" class="mt-1 text-xs text-gray-500">
            {{ t('commercial.geo.dragHint') }}
        </p>

        <div v-if="!readonly" class="mt-2 flex items-center gap-4">
            <MalioButton
                variant="secondary"
                :label="t('commercial.geo.regeocode')"
                :disabled="regeocoding || !canRegeocode"
                data-testid="geo-regeocode"
                @click="regeocode"
            />
            <span v-if="regeocodeFailed" class="text-xs text-red-600" data-testid="geo-regeocode-failed">
                {{ t('commercial.geo.regeocodeFailed') }}
            </span>
        </div>
    </div>
</template>

<script setup lang="ts">
import type { Map as LeafletMap, Marker } from 'leaflet'
import { useAddressAutocomplete } from '~/shared/composables/useAddressAutocomplete'

/**
 * Mini-carte d'ajustement du pin d'une adresse Tiers (M6.1, spec § 8.3).
 *
 * - Marqueur deplacable : au drag, emet les coordonnees corrigees avec
 *   geoManual = true (RG-6.08 : le geocodage auto ne reecrira plus). Le parent
 *   met a jour le brouillon ; la persistance suit le submit du formulaire
 *   (POST/PATCH de l'adresse), comme tous les champs du bloc.
 * - « Re-geocoder depuis l'adresse » : previsualise la position BAN cote front
 *   et emet geoManual = false — au save, le back (BanGeocoder) refait autorite
 *   et pose geocodedAt.
 * - Sans coordonnees : pas de carte, badge « a geolocaliser ».
 */
const props = defineProps<{
    /** Latitude WGS84 (chaine decimale) ou null si non geolocalisee. */
    latitude: string | null
    /** Longitude WGS84 (chaine decimale) ou null si non geolocalisee. */
    longitude: string | null
    /** RG-6.08 : pin deja corrige a la main. */
    geoManual: boolean
    /** Adresse postale a re-geocoder (« rue, code postal ville »). */
    geocodeQuery: string | null
    readonly?: boolean
}>()

const emit = defineEmits<{
    /** Nouveau positionnement du pin (drag manuel ou re-geocodage previsualise). */
    'update:coords': [value: { latitude: string, longitude: string, geoManual: boolean }]
}>()

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

const mapEl = ref<HTMLElement | null>(null)
const regeocoding = ref(false)
const regeocodeFailed = ref(false)

const hasCoords = computed(() =>
    props.latitude !== null && props.latitude !== ''
    && props.longitude !== null && props.longitude !== '',
)

const canRegeocode = computed(() => (props.geocodeQuery ?? '').trim().length >= 3)

// Instances Leaflet (hors reactivite Vue : un proxy sur la Map casse Leaflet).
let map: LeafletMap | null = null
let marker: Marker | null = null

/** Zoom d'affichage du pin (niveau rue). */
const PIN_ZOOM = 16

/**
 * Monte la carte Leaflet dans le conteneur (import dynamique : la lib n'est
 * chargee que si l'adresse a des coordonnees).
 */
async function ensureMap(): Promise<void> {
    if (map !== null || mapEl.value === null || !hasCoords.value) {
        return
    }

    const mod = await import('leaflet')
    const L = mod.default ?? mod
    await import('leaflet/dist/leaflet.css')

    // Le conteneur peut avoir disparu pendant le chargement async (v-if).
    if (mapEl.value === null) {
        return
    }

    const position: [number, number] = [Number(props.latitude), Number(props.longitude)]

    map = L.map(mapEl.value, { scrollWheelZoom: false }).setView(position, PIN_ZOOM)
    L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
        maxZoom: 19,
    }).addTo(map)

    // divIcon SVG inline : evite les assets PNG de Leaflet (chemins casses par
    // le bundler Vite sans configuration dediee).
    const icon = L.divIcon({
        className: '',
        html: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="28" height="40" fill="#2563eb" stroke="#1e40af" stroke-width="0.5"><path d="M12 0C7 0 3 4 3 9c0 6.6 9 15 9 15s9-8.4 9-15c0-5-4-9-9-9zm0 12.5A3.5 3.5 0 1 1 12 5.5a3.5 3.5 0 0 1 0 7z"/></svg>',
        iconSize: [28, 40],
        iconAnchor: [14, 40],
    })

    marker = L.marker(position, { icon, draggable: !props.readonly }).addTo(map)
    marker.on('dragend', onMarkerDragEnd)
}

/** Drag du pin -> coordonnees corrigees + geoManual (RG-6.08). */
function onMarkerDragEnd(): void {
    if (marker === null) {
        return
    }
    const position = marker.getLatLng()
    emit('update:coords', {
        latitude: position.lat.toFixed(7),
        longitude: position.lng.toFixed(7),
        geoManual: true,
    })
}

/**
 * « Re-geocoder depuis l'adresse » : previsualisation BAN cote front. Emet
 * geoManual = false — le geocodage serveur refait autorite au save.
 */
async function regeocode(): Promise<void> {
    regeocodeFailed.value = false
    const query = (props.geocodeQuery ?? '').trim()
    if (query.length < 3) {
        regeocodeFailed.value = true
        return
    }

    regeocoding.value = true
    try {
        const coords = await autocomplete.geocode(query)
        if (coords === null) {
            regeocodeFailed.value = true
            return
        }
        emit('update:coords', { ...coords, geoManual: false })
    }
    catch {
        // BAN indisponible : position inchangee, message inline.
        regeocodeFailed.value = true
    }
    finally {
        regeocoding.value = false
    }
}

// Coordonnees modifiees par le parent (drag deja applique, re-geocodage,
// rechargement) : recale le marqueur, ou monte la carte si elle n'existe pas
// encore (premieres coordonnees d'une adresse « a geolocaliser »).
watch(
    () => [props.latitude, props.longitude] as const,
    async () => {
        if (!hasCoords.value) {
            return
        }
        if (map === null) {
            await nextTick()
            await ensureMap()
            return
        }
        const position: [number, number] = [Number(props.latitude), Number(props.longitude)]
        marker?.setLatLng(position)
        map.panTo(position)
    },
)

onMounted(ensureMap)

onBeforeUnmount(() => {
    map?.remove()
    map = null
    marker = null
})
</script>
