<!--
  JobMapModule — carte du job (repère) + tracé GPS réel (Traccar) + liens géo externes (Google Maps / Street View).
  Module RÉUTILISABLE extrait du volet jobDetail (PlanificationPage). Autonome : MapLibre/OSM auto-hébergé via createPlanMap,
  tracé via roster.traccarTrack. Carte repliée par défaut (non requise pour lire le ticket) — dépliable au besoin.
  Les points de contexte « à proximité » sont OPTIONNELS (prop `nearby`) : le board les fournit, le détail ticket non.
-->
<template>
  <div class="q-mb-sm">
    <div v-if="hasCoords" class="jd-meta q-mb-xs">
      <div class="jd-geolinks">
        <a :href="mapsUrl" target="_blank" rel="noopener" class="jd-geolink"><q-icon name="satellite_alt" size="14px" /> Carte / satellite</a>
        <a :href="streetViewUrl" target="_blank" rel="noopener" class="jd-geolink"><q-icon name="streetview" size="14px" /> Street View</a>
      </div>
    </div>
    <q-btn flat dense no-caps size="sm" color="grey-7" :icon="show ? 'expand_less' : 'map'"
      :label="show ? 'Masquer la carte' : 'Voir la carte / tracé GPS'" @click="show = !show" />
    <div v-show="show">
      <div ref="mapEl" class="jd-map"></div>
      <!-- Itinéraire routier RÉEL (OSRM auto-hébergé) tracé DANS la carte MapLibre — départ = position live du tech
           assigné, sinon géoloc du navigateur. Le lien Google reste dispo pour la navigation vocale sur le téléphone. -->
      <div v-if="hasCoords" class="jd-track-row">
        <q-toggle v-model="showRoute" dense size="sm" color="primary" />
        <span class="text-caption text-weight-medium">Itinéraire routier</span>
        <span class="text-caption text-grey-6">{{ routeMsg }}</span>
        <a :href="navUrl" target="_blank" rel="noopener" class="jd-geolink q-ml-sm"><q-icon name="navigation" size="13px" /> Navigation</a>
      </div>
      <div v-if="canTrack" class="jd-track-row">
        <q-toggle v-model="showTrack" dense size="sm" color="warning" />
        <span class="text-caption text-weight-medium">Tracé GPS réel (Traccar)</span>
        <span class="text-caption text-grey-6">{{ trackMsg }}</span>
      </div>
      <div v-else class="text-caption text-grey-5 q-mb-sm">Tracé GPS réel : aujourd'hui / hier seulement (Traccar).</div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, onBeforeUnmount, nextTick } from 'vue'
import { createPlanMap } from 'src/config/basemap'
import * as roster from 'src/api/roster'

const props = defineProps({
  name: { type: String, default: '' }, // docname (non utilisé pour le rendu ; réservé)
  lat: { type: [Number, String], default: null },
  lon: { type: [Number, String], default: null },
  techId: { type: String, default: '' }, // pour le tracé GPS
  iso: { type: String, default: '' }, // jour du tracé (YYYY-MM-DD)
  canTrack: { type: Boolean, default: false }, // Traccar dispo (aujourd'hui/hier seulement)
  todayIso: { type: String, default: '' }, // pour borner le tracé au « maintenant » si c'est aujourd'hui
  nearby: { type: Array, default: () => [] }, // points pâles de contexte : [[lon, lat, subject], …]
})

const mapEl = ref(null); let _map = null
const show = ref(false)
const showTrack = ref(false); const trackMsg = ref('')
const showRoute = ref(false); const routeMsg = ref('')

const laNum = computed(() => +props.lat); const loNum = computed(() => +props.lon)
const hasCoords = computed(() => isFinite(laNum.value) && isFinite(loNum.value) && (laNum.value !== 0 || loNum.value !== 0))
const mapsUrl = computed(() => `https://www.google.com/maps/search/?api=1&query=${props.lat},${props.lon}`)
const streetViewUrl = computed(() => `https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${props.lat},${props.lon}`)
const navUrl = computed(() => `https://www.google.com/maps/dir/?api=1&destination=${props.lat},${props.lon}&travelmode=driving`) // navigation vocale (téléphone)

async function initMap () {
  if (!mapEl.value) return
  if (_map) { try { _map.remove() } catch (e) {} _map = null }
  const hasLL = hasCoords.value
  const mapboxgl = window.mapboxgl // installé par createPlanMap
  _map = createPlanMap(mapEl.value, { center: hasLL ? [loNum.value, laNum.value] : [-73.6756, 45.1599], zoom: hasLL ? 14 : 9 })
  _map.on('load', () => {
    _map.resize()
    if (hasLL && props.nearby.length) {
      _map.addSource('jd-near', { type: 'geojson', data: { type: 'FeatureCollection', features: props.nearby.map(p => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [p[0], p[1]] }, properties: { t: p[2] } })) } })
      _map.addLayer({ id: 'jd-near', type: 'circle', source: 'jd-near', paint: { 'circle-radius': 5, 'circle-color': '#94a3b8', 'circle-opacity': 0.45, 'circle-stroke-color': '#fff', 'circle-stroke-width': 1 } })
      _map.on('mouseenter', 'jd-near', () => { _map.getCanvas().style.cursor = 'pointer' })
      _map.on('mouseleave', 'jd-near', () => { _map.getCanvas().style.cursor = '' })
      _map.on('click', 'jd-near', (e) => { const p = e.features[0].properties; new mapboxgl.Popup({ offset: 10 }).setLngLat(e.features[0].geometry.coordinates).setHTML('<div style="font-size:11px;color:#475569">' + String(p.t || '').replace(/[<>&]/g, '') + '</div>').addTo(_map) })
    }
    if (hasLL) new mapboxgl.Marker({ color: '#1976d2' }).setLngLat([loNum.value, laNum.value]).addTo(_map)
    // Itinéraire routier (OSRM) — sous le tracé GPS réel
    _map.addSource('jd-route', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } })
    _map.addLayer({ id: 'jd-route-halo', type: 'line', source: 'jd-route', layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#2563eb', 'line-width': 9, 'line-opacity': 0.2 } })
    _map.addLayer({ id: 'jd-route-l', type: 'line', source: 'jd-route', layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#2563eb', 'line-width': 4, 'line-opacity': 0.85 } })
    _map.addSource('jd-track', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } })
    _map.addLayer({ id: 'jd-track-halo', type: 'line', source: 'jd-track', layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#ff6f00', 'line-width': 8, 'line-opacity': 0.22 } })
    _map.addLayer({ id: 'jd-track-l', type: 'line', source: 'jd-track', layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#ef6c00', 'line-width': 3, 'line-opacity': 0.78 } })
    if (showRoute.value) loadRoute()
    if (showTrack.value) loadTrack()
  })
}
async function loadTrack () {
  trackMsg.value = '…'
  const iso = props.iso; if (!iso || !props.techId || !_map) { trackMsg.value = ''; return }
  const from = new Date(iso + 'T00:00:00').toISOString()
  const to = (props.todayIso && iso === props.todayIso) ? new Date().toISOString() : new Date(iso + 'T23:59:59').toISOString()
  try {
    const r = await roster.traccarTrack(props.techId, from, to); const coords = (r && r.coords) || []
    const src = _map.getSource('jd-track')
    if (src) src.setData(coords.length >= 2 ? { type: 'Feature', geometry: { type: 'LineString', coordinates: coords }, properties: {} } : { type: 'FeatureCollection', features: [] })
    trackMsg.value = coords.length >= 2 ? (coords.length + ' points GPS') : "Aucun tracé ce jour (ou pas d'appareil)."
  } catch (e) { trackMsg.value = 'Tracé indisponible' }
}

// Géoloc navigateur (repli si pas de tech assigné / pas de GPS live).
function browserGeo () { return new Promise(res => { if (!navigator.geolocation) return res(null); navigator.geolocation.getCurrentPosition(p => res([p.coords.longitude, p.coords.latitude]), () => res(null), { enableHighAccuracy: true, timeout: 8000 }) }) }
// Itinéraire routier RÉEL via l'OSRM auto-hébergé (roster.osrmRoute) → tracé DANS la carte MapLibre + km/min.
// Départ : position live du tech assigné (Traccar), sinon la position du navigateur. Destination = coords du service.
async function loadRoute () {
  if (!_map || !hasCoords.value) { routeMsg.value = ''; return }
  routeMsg.value = 'calcul…'
  let start = null, from = ''
  if (props.techId) {
    try { const p = await roster.techLivePosition(props.techId); const la = +(p && (p.lat != null ? p.lat : p.latitude)); const lo = +(p && (p.lon != null ? p.lon : p.longitude)); if (isFinite(la) && isFinite(lo) && (la || lo)) { start = [lo, la]; from = '· du technicien' } } catch (e) {}
  }
  if (!start) { const g = await browserGeo(); if (g) { start = g; from = '· de ma position' } }
  if (!start) { routeMsg.value = 'position de départ indisponible (autorise la géolocalisation)'; return }
  try {
    const r = await roster.osrmRoute([start, [loNum.value, laNum.value]])
    const src = _map && _map.getSource('jd-route')
    if (r && r.geometry && src) {
      src.setData({ type: 'Feature', geometry: r.geometry, properties: {} })
      routeMsg.value = `${r.km} km · ${r.mins} min ${from}`
      try { const b = new window.mapboxgl.LngLatBounds(); (r.geometry.coordinates || []).forEach(c => b.extend(c)); if (!b.isEmpty()) _map.fitBounds(b, { padding: 40, maxZoom: 15 }) } catch (e) {}
    } else routeMsg.value = 'itinéraire indisponible'
  } catch (e) { routeMsg.value = 'itinéraire indisponible' }
}
function destroy () { if (_map) { try { _map.remove() } catch (e) {} _map = null } }
watch(show, (on) => { if (on) nextTick(() => setTimeout(initMap, 200)); else destroy() })
watch(showTrack, (on) => { if (!_map) return; if (on) loadTrack(); else { const s = _map.getSource('jd-track'); if (s) s.setData({ type: 'FeatureCollection', features: [] }); trackMsg.value = '' } })
watch(showRoute, (on) => { if (!_map) return; if (on) loadRoute(); else { const s = _map.getSource('jd-route'); if (s) s.setData({ type: 'FeatureCollection', features: [] }); routeMsg.value = ''; if (hasCoords.value) _map.flyTo({ center: [loNum.value, laNum.value], zoom: 14 }) } })
onBeforeUnmount(destroy)
</script>

<style scoped>
.jd-meta { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: #455; }
.jd-geolinks { display: flex; padding-left: 22px; gap: 14px; }
.jd-geolink { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; color: #2563eb; text-decoration: none; font-weight: 500; }
.jd-geolink:hover { text-decoration: underline; }
.jd-map { width: 100%; height: 240px; border-radius: 8px; overflow: hidden; margin-bottom: 8px; background: #eceff3; }
.jd-track-row { display: flex; align-items: center; gap: 6px; margin: -2px 0 8px; }
</style>
