<template>
  <NuxtLayout name="map">
    <template #sidebar>
      <MapSidebar
        :active-tab="activeTab"
        :hubs="hubs"
        :suppliers="suppliers"
        :offers="offers"
        :selected-item-id="selectedItemId"
        :is-loading="isLoading"
        @update:active-tab="activeTab = $event"
        @select="selectItem"
      />
    </template>

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

<script setup lang="ts">
import type { Map as MapboxMapType } from 'mapbox-gl'
import { GetSuppliersDocument, GetOffersDocument } from '~/composables/graphql/public/exchange-generated'
import { GetNodesDocument } from '~/composables/graphql/public/geo-generated'

definePageMeta({
  layout: false
})

const { t } = useI18n()

const mapRef = useMapboxRef('catalog-map')

const activeTab = ref<'hubs' | 'suppliers' | 'offers'>('hubs')
const selectedItemId = ref<string | null>(null)
const isLoading = computed(() => hubsPending.value || suppliersPending.value || offersPending.value)
const isAnimating = ref(false)

const { data: hubsData, pending: hubsPending } = await useServerQuery('catalog-map-hubs', GetNodesDocument, {}, 'public', 'geo')
const hubs = computed(() => (hubsData.value?.nodes || []).filter((h: any) => h.latitude && h.longitude))

const { data: suppliersData, pending: suppliersPending } = await useServerQuery('catalog-map-suppliers', GetSuppliersDocument, {}, 'public', 'exchange')
const suppliers = computed(() => suppliersData.value?.getSuppliers || [])

const { data: offersData, pending: offersPending } = await useServerQuery('catalog-map-offers', GetOffersDocument, {}, 'public', 'exchange')
const offers = computed(() => offersData.value?.getOffers || [])

const mapOptions = {
  style: 'mapbox://styles/mapbox/satellite-streets-v12',
  center: [37.64, 55.76] as [number, number],
  zoom: 2,
  projection: 'globe',
  pitch: 20
}

// GeoJSON data for the map
const geoJsonData = computed(() => ({
  type: 'FeatureCollection' as const,
  features: [
    ...hubs.value.filter(h => h.latitude && h.longitude).map(hub => ({
      type: 'Feature' as const,
      properties: { uuid: hub.uuid, name: hub.name, country: hub.country, itemType: 'hub' },
      geometry: { type: 'Point' as const, coordinates: [hub.longitude, hub.latitude] }
    })),
    ...suppliers.value.filter(s => s.latitude && s.longitude).map(supplier => ({
      type: 'Feature' as const,
      properties: { uuid: supplier.uuid, name: supplier.name, country: supplier.country, itemType: 'supplier' },
      geometry: { type: 'Point' as const, coordinates: [supplier.longitude, supplier.latitude] }
    })),
    ...offers.value.filter(o => o.locationLatitude && o.locationLongitude).map(offer => ({
      type: 'Feature' as const,
      properties: { uuid: offer.uuid, name: offer.productName, country: offer.locationCountry, itemType: 'offer' },
      geometry: { type: 'Point' as const, coordinates: [offer.locationLongitude, offer.locationLatitude] }
    }))
  ]
}))

const onMapCreated = (map: MapboxMapType) => {
  const initMap = () => {
    // Globe atmosphere
    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
    })

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

    // Cluster circles
    map.addLayer({
      id: 'clusters',
      type: 'circle',
      source: 'catalog-points',
      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
    map.addLayer({
      id: 'cluster-count',
      type: 'symbol',
      source: 'catalog-points',
      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: 'catalog-points',
      filter: ['!', ['has', 'point_count']],
      paint: {
        'circle-radius': 12,
        'circle-color': '#10b981',
        'circle-stroke-width': 3,
        'circle-stroke-color': '#ffffff'
      }
    })

    // Labels
    map.addLayer({
      id: 'point-labels',
      type: 'symbol',
      source: 'catalog-points',
      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
      }
    })

    // Click handlers
    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('catalog-points') 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
      const props = features[0].properties
      selectedItemId.value = props?.uuid
      if (props?.itemType === 'hub') activeTab.value = 'hubs'
      else if (props?.itemType === 'supplier') activeTab.value = 'suppliers'
      else if (props?.itemType === 'offer') activeTab.value = 'offers'
    })

    // Cursor
    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 points when data changes
watch(geoJsonData, (newData) => {
  if (!mapRef.value) return
  const source = mapRef.value.getSource('catalog-points') as mapboxgl.GeoJSONSource | undefined
  if (source) {
    source.setData(newData)
  }
}, { deep: true })

const selectItem = async (item: any, type: string) => {
  selectedItemId.value = item.uuid
  const lat = item.latitude || item.locationLatitude
  const lng = item.longitude || item.locationLongitude

  if (mapRef.value && lat && lng && !isAnimating.value) {
    isAnimating.value = true
    const map = mapRef.value
    const currentCenter = map.getCenter()

    // Phase 1: zoom out
    await new Promise<void>(resolve => {
      map.easeTo({
        center: [(currentCenter.lng + lng) / 2, (currentCenter.lat + lat) / 2],
        zoom: 1.5,
        duration: 2000,
        easing: (t) => t * (2 - t)
      })
      setTimeout(resolve, 2000)
    })

    // Phase 2: fly to target
    await new Promise<void>(resolve => {
      map.easeTo({
        center: [lng, lat],
        zoom: 8,
        duration: 3000,
        easing: (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
      })
      setTimeout(resolve, 3000)
    })

    isAnimating.value = false
  }
}
</script>
