<template>
  <div class="flex flex-col flex-1 min-h-0 w-full h-full">
    <ClientOnly>
      <MapboxMap
        :map-id="mapId"
        class="flex-1 min-h-0"
        style="width: 100%; height: 100%"
        :options="mapOptions"
        @load="onMapCreated"
      >
        <MapboxNavigationControl position="top-right" />
      </MapboxMap>
    </ClientOnly>
  </div>
</template>

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

interface MapItem {
  uuid: string
  name: string
  latitude: number
  longitude: number
  country?: string
}

const props = withDefaults(defineProps<{
  mapId: string
  items: MapItem[]
  pointColor?: string
  initialCenter?: [number, number]
  initialZoom?: number
}>(), {
  pointColor: '#10b981',
  initialCenter: () => [37.64, 55.76],
  initialZoom: 2
})

const emit = defineEmits<{
  'select-item': [uuid: string]
}>()

const mapRef = useMapboxRef(props.mapId)
const { flyThroughSpace } = useMapboxFlyAnimation()

const mapOptions = computed(() => ({
  style: 'mapbox://styles/mapbox/satellite-streets-v12',
  center: props.initialCenter,
  zoom: props.initialZoom,
  projection: 'globe',
  pitch: 20
}))

const geoJsonData = computed(() => ({
  type: 'FeatureCollection' as const,
  features: props.items.map(item => ({
    type: 'Feature' as const,
    properties: { uuid: item.uuid, name: item.name, country: item.country },
    geometry: { type: 'Point' as const, coordinates: [item.longitude, item.latitude] }
  }))
}))

const sourceId = computed(() => `${props.mapId}-points`)

const onMapCreated = (map: MapboxMapType) => {
  const initMap = () => {
    map.setFog({
      color: 'rgb(186, 210, 235)',
      'high-color': 'rgb(36, 92, 223)',
      'horizon-blend': 0.02,
      'space-color': 'rgb(11, 11, 25)',
      'star-intensity': 0.6
    })

    map.addSource(sourceId.value, {
      type: 'geojson',
      data: geoJsonData.value,
      cluster: true,
      clusterMaxZoom: 14,
      clusterRadius: 50
    })

    map.addLayer({
      id: 'clusters',
      type: 'circle',
      source: sourceId.value,
      filter: ['has', 'point_count'],
      paint: {
        'circle-color': props.pointColor,
        'circle-radius': ['step', ['get', 'point_count'], 20, 10, 30, 50, 40],
        'circle-opacity': 0.8,
        'circle-stroke-width': 2,
        'circle-stroke-color': '#ffffff'
      }
    })

    map.addLayer({
      id: 'cluster-count',
      type: 'symbol',
      source: sourceId.value,
      filter: ['has', 'point_count'],
      layout: {
        'text-field': ['get', 'point_count_abbreviated'],
        'text-size': 14
      },
      paint: { 'text-color': '#ffffff' }
    })

    map.addLayer({
      id: 'unclustered-point',
      type: 'circle',
      source: sourceId.value,
      filter: ['!', ['has', 'point_count']],
      paint: {
        'circle-radius': 12,
        'circle-color': props.pointColor,
        'circle-stroke-width': 3,
        'circle-stroke-color': '#ffffff'
      }
    })

    map.addLayer({
      id: 'point-labels',
      type: 'symbol',
      source: sourceId.value,
      filter: ['!', ['has', 'point_count']],
      layout: {
        'text-field': ['get', 'name'],
        'text-offset': [0, 1.5],
        'text-size': 12,
        'text-font': ['Open Sans Bold', 'Arial Unicode MS Bold']
      },
      paint: {
        'text-color': '#ffffff',
        'text-halo-color': '#000000',
        'text-halo-width': 1.5
      }
    })

    map.on('click', 'clusters', (e) => {
      const features = map.queryRenderedFeatures(e.point, { layers: ['clusters'] })
      if (!features.length) return
      const clusterId = features[0].properties?.cluster_id
      const source = map.getSource(sourceId.value) as mapboxgl.GeoJSONSource
      source.getClusterExpansionZoom(clusterId, (err, zoom) => {
        if (err) return
        const geometry = features[0].geometry as GeoJSON.Point
        map.easeTo({ center: geometry.coordinates as [number, number], zoom: zoom || 4 })
      })
    })

    map.on('click', 'unclustered-point', (e) => {
      const features = map.queryRenderedFeatures(e.point, { layers: ['unclustered-point'] })
      if (!features.length) return
      emit('select-item', features[0].properties?.uuid)
    })

    map.on('mouseenter', 'clusters', () => { map.getCanvas().style.cursor = 'pointer' })
    map.on('mouseleave', 'clusters', () => { map.getCanvas().style.cursor = '' })
    map.on('mouseenter', 'unclustered-point', () => { map.getCanvas().style.cursor = 'pointer' })
    map.on('mouseleave', 'unclustered-point', () => { map.getCanvas().style.cursor = '' })
  }

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

// Update map data when items change
watch(geoJsonData, (newData) => {
  if (!mapRef.value) return
  const source = mapRef.value.getSource(sourceId.value) as mapboxgl.GeoJSONSource | undefined
  if (source) {
    source.setData(newData)
  }
}, { deep: true })

// Expose flyTo method for external use (with space fly animation)
const flyTo = async (lat: number, lng: number, zoom = 8) => {
  if (!mapRef.value) return
  await flyThroughSpace(mapRef.value, {
    targetCenter: [lng, lat],
    targetZoom: zoom,
    totalDuration: 5000,
    minZoom: 3
  })
}

defineExpose({ flyTo })
</script>
