<template>
  <div class="mapbox-globe-wrapper rounded-medium overflow-hidden" :style="{ height: `${height}px` }">
    <MapboxMap
      :map-id="mapId"
      style="width: 100%; height: 100%"
      :options="mapOptions"
      @mb-created="onMapCreated"
    >
      <MapboxNavigationControl position="top-right" />
    </MapboxMap>

    <!-- Location info popup -->
    <Transition
      enter-active-class="transition ease-out duration-200"
      enter-from-class="opacity-0 translate-y-2"
      enter-to-class="opacity-100 translate-y-0"
      leave-active-class="transition ease-in duration-150"
      leave-from-class="opacity-100 translate-y-0"
      leave-to-class="opacity-0 translate-y-2"
    >
      <div
        v-if="selectedLocation"
        class="absolute bottom-4 left-4 right-4 sm:left-auto sm:right-4 sm:w-80 bg-white rounded-medium shadow-lg p-4 z-10"
      >
        <Stack gap="3">
          <Stack direction="row" align="center" justify="between">
            <Stack gap="1">
              <Heading :level="4" weight="semibold">{{ selectedLocation.name }}</Heading>
              <Text tone="muted" size="base">{{ selectedLocation.country }}</Text>
            </Stack>
            <button
              class="p-1 text-gray-400 hover:text-gray-600 transition-colors"
              @click="selectedLocation = null"
            >
              <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
              </svg>
            </button>
          </Stack>
          <Button size="small" @click="flyToSelectedLocation">
            Перелететь сюда
          </Button>
        </Stack>
      </div>
    </Transition>

    <!-- Animation indicator -->
    <Transition
      enter-active-class="transition ease-out duration-200"
      enter-from-class="opacity-0"
      enter-to-class="opacity-100"
      leave-active-class="transition ease-in duration-150"
      leave-from-class="opacity-100"
      leave-to-class="opacity-0"
    >
      <div
        v-if="isAnimating"
        class="absolute top-4 left-1/2 -translate-x-1/2 bg-black/70 text-white px-4 py-2 rounded-full text-sm flex items-center gap-2"
      >
        <svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
          <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
          <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
        </svg>
        Перелет...
      </div>
    </Transition>
  </div>
</template>

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

interface Location {
  uuid?: string | null
  name?: string | null
  latitude?: number | null
  longitude?: number | null
  country?: string | null
}

const props = withDefaults(defineProps<{
  locations: Location[]
  height?: number
  initialCenter?: [number, number]
  initialZoom?: number
  selectedLocationId?: string | null
}>(), {
  height: 500,
  initialCenter: () => [37.64, 55.76], // Moscow
  initialZoom: 2,
  selectedLocationId: null
})

const emit = defineEmits<{
  (e: 'location-click', location: Location): void
}>()

const mapId = 'globe-map'
const mapInstance = ref<MapboxMap | null>(null)
const selectedLocation = ref<Location | null>(null)

const { flyThroughSpace, setupGlobeAtmosphere, isAnimating } = 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.locations
    .filter(loc => loc.latitude != null && loc.longitude != null)
    .map(location => ({
      type: 'Feature' as const,
      properties: {
        uuid: location.uuid,
        name: location.name,
        country: location.country
      },
      geometry: {
        type: 'Point' as const,
        coordinates: [location.longitude!, location.latitude!]
      }
    }))
}))

const onMapCreated = (map: MapboxMap) => {
  mapInstance.value = map

  map.on('load', () => {
    // Setup globe atmosphere with stars
    setupGlobeAtmosphere(map)

    // Add clustered source
    map.addSource('locations', {
      type: 'geojson',
      data: geoJsonData.value,
      cluster: true,
      clusterMaxZoom: 14,
      clusterRadius: 50
    })

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

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

    // Individual points
    map.addLayer({
      id: 'unclustered-point',
      type: 'circle',
      source: 'locations',
      filter: ['!', ['has', 'point_count']],
      paint: {
        'circle-radius': [
          'case',
          ['==', ['get', 'uuid'], props.selectedLocationId || ''],
          14,
          10
        ],
        'circle-color': [
          'case',
          ['==', ['get', 'uuid'], props.selectedLocationId || ''],
          '#f59e0b',
          '#10b981'
        ],
        'circle-stroke-width': 2,
        'circle-stroke-color': '#ffffff'
      }
    })

    // Click on cluster to zoom in
    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('locations') 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
        })
      })
    })

    // Click on individual point
    map.on('click', 'unclustered-point', (e) => {
      const features = map.queryRenderedFeatures(e.point, { layers: ['unclustered-point'] })
      if (!features.length) return

      const props = features[0].properties
      const geometry = features[0].geometry as GeoJSON.Point

      const location: Location = {
        uuid: props?.uuid,
        name: props?.name,
        country: props?.country,
        longitude: geometry.coordinates[0],
        latitude: geometry.coordinates[1]
      }

      selectedLocation.value = location
      emit('location-click', location)
    })

    // Cursor changes
    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 = ''
    })
  })
}

const flyToSelectedLocation = async () => {
  if (!mapInstance.value || !selectedLocation.value) return

  const { longitude, latitude } = selectedLocation.value
  if (longitude == null || latitude == null) return

  await flyThroughSpace(mapInstance.value, {
    targetCenter: [longitude, latitude],
    targetZoom: 8,
    totalDuration: 6000
  })

  selectedLocation.value = null
}

// Update source data when locations change
watch(geoJsonData, (newData) => {
  if (!mapInstance.value) return

  const source = mapInstance.value.getSource('locations') as mapboxgl.GeoJSONSource | undefined
  if (source) {
    source.setData(newData)
  }
}, { deep: true })

// Update marker styles when selectedLocationId changes
watch(() => props.selectedLocationId, (newId) => {
  if (!mapInstance.value) return

  const map = mapInstance.value

  // Update circle radius
  map.setPaintProperty('unclustered-point', 'circle-radius', [
    'case',
    ['==', ['get', 'uuid'], newId || ''],
    14,
    10
  ])

  // Update circle color
  map.setPaintProperty('unclustered-point', 'circle-color', [
    'case',
    ['==', ['get', 'uuid'], newId || ''],
    '#f59e0b',
    '#10b981'
  ])
})

// Fly to a specific location (exposed for parent component)
const flyToLocation = async (location: Location) => {
  if (!mapInstance.value) return

  const { longitude, latitude } = location
  if (longitude == null || latitude == null) return

  selectedLocation.value = location

  await flyThroughSpace(mapInstance.value, {
    targetCenter: [longitude, latitude],
    targetZoom: 8,
    totalDuration: 6000
  })
}

// Expose methods for parent component
defineExpose({
  flyToLocation
})
</script>

<style scoped>
.mapbox-globe-wrapper {
  position: relative;
}
</style>
