<template>
  <div class="relative w-full rounded-box overflow-hidden" :style="{ height: computedHeight }">
    <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-base-100 border border-base-300 rounded-box 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-base-content/60 hover:text-base-content 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">
            Fly here
          </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-base-content/70 text-base-100 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>
        Flying...
      </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 | string
  initialCenter?: [number, number]
  initialZoom?: number
  selectedLocationId?: string | null
  mapId?: string
}>(), {
  height: 500,
  initialCenter: () => [37.64, 55.76], // Moscow
  initialZoom: 2,
  selectedLocationId: null,
  mapId: 'globe-map'
})

// Compute height: if number add px, else use as provided
const computedHeight = computed(() => {
  if (typeof props.height === 'number') {
    return `${props.height}px`
  }
  return props.height
})

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

// useMapboxRef gives reactive map ref when ready
const mapRef = useMapboxRef(props.mapId)
const selectedLocation = ref<Location | null>(null)

// Promise to wait for map readiness
let mapReadyResolve: (() => void) | null = null
const mapReadyPromise = new Promise<void>((resolve) => {
  mapReadyResolve = resolve
})
const isMapReady = ref(false)

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) => {
  console.log('[MapboxGlobe] onMapCreated, map.loaded():', map.loaded())

  const initMap = () => {
    console.log('[MapboxGlobe] initMap called')

    // Setup globe atmosphere with stars
    setupGlobeAtmosphere(map)

    console.log('[MapboxGlobe] Map loaded, locations:', props.locations.length)
    console.log('[MapboxGlobe] GeoJSON features:', geoJsonData.value.features.length)

    // 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 || '__NONE__'],
          14,
          10
        ],
        'circle-color': [
          'case',
          ['==', ['get', 'uuid'], props.selectedLocationId || '__NONE__'],
          '#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 featureProps = features[0].properties
      const geometry = features[0].geometry as GeoJSON.Point

      const location: Location = {
        uuid: featureProps?.uuid,
        name: featureProps?.name,
        country: featureProps?.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 = ''
    })
  }

  // If map already loaded - init immediately, otherwise wait for load
  if (map.loaded()) {
    initMap()
    isMapReady.value = true
    mapReadyResolve?.()
  } else {
    map.on('load', () => {
      initMap()
      isMapReady.value = true
      mapReadyResolve?.()
    })
  }
}

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

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

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

  selectedLocation.value = null
}

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

  const source = mapRef.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 (!mapRef.value || !isMapReady.value) return

  const map = mapRef.value

  // Check if layer exists before updating
  if (!map.getLayer('unclustered-point')) return

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

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

// Fly to a specific location (exposed for parent component)
const flyToLocation = async (location: Location) => {
  console.log('[MapboxGlobe] flyToLocation called:', location.name, location.longitude, location.latitude)
  console.log('[MapboxGlobe] isMapReady:', isMapReady.value, 'mapRef.value:', !!mapRef.value)

  const { longitude, latitude } = location
  if (longitude == null || latitude == null) {
    console.log('[MapboxGlobe] ERROR: coordinates are null')
    return
  }

  // Wait for map readiness if not ready
  if (!isMapReady.value) {
    console.log('[MapboxGlobe] waiting for map to be ready...')
    await mapReadyPromise
    console.log('[MapboxGlobe] map is now ready!')
  }

  if (!mapRef.value) {
    console.log('[MapboxGlobe] ERROR: mapRef is still null after waiting')
    return
  }

  selectedLocation.value = location

  console.log('[MapboxGlobe] calling flyThroughSpace:', [longitude, latitude])
  // Space fly animation settings: 5000ms, minZoom 3, targetZoom 12
  await flyThroughSpace(mapRef.value, {
    targetCenter: [longitude, latitude],
    targetZoom: 12,
    totalDuration: 5000,
    minZoom: 3
  })
  console.log('[MapboxGlobe] flyThroughSpace completed')
}

// Expose methods for parent component
defineExpose({
  flyToLocation,
  resize: () => mapRef.value?.resize(),
  isMapReady,
  waitForReady: () => mapReadyPromise
})
</script>
