<template>
  <div class="w-full">
    <MapboxMap
      :key="mapId"
      :map-id="mapId"
      :style="`height: ${height}px; width: 100%;`"
      class="rounded-lg"
      :options="mapOptions"
      @load="onMapCreated"
    >
      <MapboxNavigationControl position="top-right" />
    </MapboxMap>
  </div>
</template>

<script setup lang="ts">
import type { PropType } from 'vue'
import { getCurrentInstance } from 'vue'
import type { Map as MapboxMapType } from 'mapbox-gl'
import { LngLatBounds } from 'mapbox-gl'

type RouteStage = {
  fromLat?: number | null
  fromLon?: number | null
  toLat?: number | null
  toLon?: number | null
  transportType?: string | null
}

type OrderRoute = {
  uuid: string
  name: string
  status?: string
  stages: RouteStage[]
}

const props = defineProps({
  routes: {
    type: Array as PropType<OrderRoute[]>,
    default: () => []
  },
  height: {
    type: Number,
    default: 192
  }
})

const mapRef = ref<MapboxMapType | null>(null)
const didFitBounds = ref(false)

const instanceId = getCurrentInstance()?.uid || Math.floor(Math.random() * 100000)
const mapId = computed(() => `orders-preview-${instanceId}`)

const statusColors: Record<string, string> = {
  pending: '#f59e0b',
  processing: '#3b82f6',
  in_transit: '#06b6d4',
  delivered: '#22c55e',
  cancelled: '#ef4444'
}

const allCoordinates = computed(() => {
  const coords: [number, number][] = []
  props.routes.forEach(order => {
    order.stages.forEach(stage => {
      if (stage.fromLat && stage.fromLon) {
        coords.push([stage.fromLon, stage.fromLat])
      }
      if (stage.toLat && stage.toLon) {
        coords.push([stage.toLon, stage.toLat])
      }
    })
  })
  return coords
})

const mapCenter = computed<[number, number]>(() => {
  const coords = allCoordinates.value
  if (!coords.length) return [50, 50]
  const avgLng = coords.reduce((sum, c) => sum + c[0], 0) / coords.length
  const avgLat = coords.reduce((sum, c) => sum + c[1], 0) / coords.length
  return [avgLng, avgLat]
})

const mapOptions = computed(() => ({
  style: 'mapbox://styles/mapbox/satellite-streets-v12',
  center: mapCenter.value,
  zoom: 2,
  interactive: false
}))

const routeLineFeatures = computed<GeoJSON.FeatureCollection<GeoJSON.LineString>>(() => {
  const features: GeoJSON.Feature<GeoJSON.LineString>[] = []

  props.routes.forEach(order => {
    order.stages.forEach(stage => {
      if (stage.fromLat && stage.fromLon && stage.toLat && stage.toLon) {
        features.push({
          type: 'Feature',
          properties: {
            orderId: order.uuid,
            status: order.status,
            color: statusColors[order.status || ''] || '#6b7280'
          },
          geometry: {
            type: 'LineString',
            coordinates: [
              [stage.fromLon, stage.fromLat],
              [stage.toLon, stage.toLat]
            ]
          }
        })
      }
    })
  })

  return { type: 'FeatureCollection', features }
})

const markersFeatureCollection = computed<GeoJSON.FeatureCollection<GeoJSON.Point>>(() => {
  const points: GeoJSON.Feature<GeoJSON.Point>[] = []
  const seen = new Set<string>()

  props.routes.forEach(order => {
    order.stages.forEach(stage => {
      if (stage.fromLat && stage.fromLon) {
        const key = `${stage.fromLon},${stage.fromLat}`
        if (!seen.has(key)) {
          seen.add(key)
          points.push({
            type: 'Feature',
            properties: { color: statusColors[order.status || ''] || '#6b7280' },
            geometry: { type: 'Point', coordinates: [stage.fromLon, stage.fromLat] }
          })
        }
      }
      if (stage.toLat && stage.toLon) {
        const key = `${stage.toLon},${stage.toLat}`
        if (!seen.has(key)) {
          seen.add(key)
          points.push({
            type: 'Feature',
            properties: { color: statusColors[order.status || ''] || '#6b7280' },
            geometry: { type: 'Point', coordinates: [stage.toLon, stage.toLat] }
          })
        }
      }
    })
  })

  return { type: 'FeatureCollection', features: points }
})

const fitMapToRoutes = () => {
  const map = mapRef.value
  if (!map || didFitBounds.value) return

  const coords = allCoordinates.value
  if (coords.length === 0) return

  const bounds = coords.reduce(
    (acc, coord) => acc.extend(coord),
    new LngLatBounds(coords[0], coords[0])
  )

  map.fitBounds(bounds, { padding: 30, maxZoom: 6 })
  didFitBounds.value = true
}

const onMapCreated = (map: MapboxMapType) => {
  mapRef.value = map

  const initMap = () => {
    map.addSource('orders-routes', {
      type: 'geojson',
      data: routeLineFeatures.value
    })

    map.addLayer({
      id: 'orders-routes-lines',
      type: 'line',
      source: 'orders-routes',
      paint: {
        'line-color': ['get', 'color'],
        'line-width': 3,
        'line-opacity': 0.8
      }
    })

    map.addSource('orders-markers', {
      type: 'geojson',
      data: markersFeatureCollection.value
    })

    map.addLayer({
      id: 'orders-markers-circles',
      type: 'circle',
      source: 'orders-markers',
      paint: {
        'circle-radius': 8,
        'circle-color': ['coalesce', ['get', 'color'], '#f97316'],
        'circle-stroke-width': 2,
        'circle-stroke-color': '#ffffff'
      }
    })

    fitMapToRoutes()
  }

  if (map.loaded()) {
    initMap()
  } else {
    map.on('load', initMap)
  }
}

watch(
  () => props.routes,
  () => {
    didFitBounds.value = false
    const map = mapRef.value
    if (!map) return

    const routesSource = map.getSource('orders-routes') as mapboxgl.GeoJSONSource | undefined
    if (routesSource) routesSource.setData(routeLineFeatures.value)

    const markersSource = map.getSource('orders-markers') as mapboxgl.GeoJSONSource | undefined
    if (markersSource) markersSource.setData(markersFeatureCollection.value)

    fitMapToRoutes()
  },
  { deep: true }
)
</script>
