<template>
  <Section variant="plain" paddingY="md">
    <Stack gap="4">
      <div v-if="autoEdges.length === 0 && railEdges.length === 0" class="text-base-content/60">
        {{ t('catalogHub.nearbyHubs.empty') }}
      </div>

      <div v-else class="grid grid-cols-1 lg:grid-cols-2 gap-4">
        <!-- Map -->
        <div class="order-2 lg:order-1">
          <ClientOnly>
            <MapboxMap
              :key="mapId"
              :map-id="mapId"
              style="height: 360px; width: 100%;"
              class="rounded-lg border border-base-300"
              :options="mapOptions"
              @load="onMapCreated"
            >
              <MapboxNavigationControl position="top-right" />
            </MapboxMap>

            <template #fallback>
              <div class="h-[360px] w-full bg-base-200 rounded-lg flex items-center justify-center">
                <Spinner />
              </div>
            </template>
          </ClientOnly>
        </div>

        <!-- Neighbors lists -->
        <div class="order-1 lg:order-2">
          <Stack gap="4">
            <div>
              <div class="flex items-center gap-2 mb-2">
                <Icon name="lucide:truck" size="20" />
                <Heading :level="3">{{ t('catalogHub.nearbyHubs.byRoad') }}</Heading>
              </div>
              <Stack v-if="autoEdges.length > 0" gap="2">
                <NuxtLink
                  v-for="(edge, index) in autoEdges"
                  :key="edge.toUuid ?? index"
                  :to="localePath(`/catalog/hubs/${edge.toUuid}`)"
                  class="flex flex-col gap-2 p-3 rounded-lg border border-base-300 hover:bg-base-200 transition-colors"
                >
                  <div class="flex items-center justify-between">
                    <div class="flex items-center gap-3">
                      <div class="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center">
                        <Icon name="lucide:map-pin" size="16" class="text-primary" />
                      </div>
                      <div>
                        <div class="font-medium">{{ edge.toName }}</div>
                      </div>
                    </div>
                    <div class="text-right">
                      <div class="font-semibold text-primary">{{ edge.distanceKm }} km</div>
                    </div>
                  </div>
                </NuxtLink>
              </Stack>
              <div v-else class="text-base-content/60">
                {{ t('catalogHub.nearbyHubs.empty') }}
              </div>
            </div>

            <div>
              <div class="flex items-center gap-2 mb-2">
                <Icon name="lucide:train" size="20" />
                <Heading :level="3">{{ t('catalogHub.nearbyHubs.byRail') }}</Heading>
              </div>
              <Stack v-if="railEdges.length > 0" gap="2">
                <NuxtLink
                  v-for="(edge, index) in railEdges"
                  :key="edge.toUuid ?? index"
                  :to="localePath(`/catalog/hubs/${edge.toUuid}`)"
                  class="flex flex-col gap-2 p-3 rounded-lg border border-base-300 hover:bg-base-200 transition-colors"
                >
                  <div class="flex items-center justify-between">
                    <div class="flex items-center gap-3">
                      <div class="w-8 h-8 rounded-full bg-emerald-500/10 flex items-center justify-center">
                        <Icon name="lucide:map-pin" size="16" class="text-emerald-500" />
                      </div>
                      <div>
                        <div class="font-medium">{{ edge.toName }}</div>
                      </div>
                    </div>
                    <div class="text-right">
                      <div class="font-semibold text-emerald-600">{{ edge.distanceKm }} km</div>
                    </div>
                  </div>
                </NuxtLink>
              </Stack>
              <div v-else class="text-base-content/60">
                {{ t('catalogHub.nearbyHubs.empty') }}
              </div>
            </div>
          </Stack>
        </div>
      </div>
    </Stack>
  </Section>
</template>

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

interface CurrentHub {
  uuid: string
  name: string
  latitude: number
  longitude: number
}

interface RouteGeometry {
  toUuid: string
  coordinates: [number, number][]
}

const props = defineProps<{
  autoEdges: EdgeType[]
  railEdges: EdgeType[]
  hub: CurrentHub
  railHub: CurrentHub
  autoRouteGeometries: RouteGeometry[]
  railRouteGeometries: RouteGeometry[]
}>()

const { t } = useI18n()
const localePath = useLocalePath()
const mapRef = ref<MapboxMapType | null>(null)
const isMapReady = ref(false)
const didFitBounds = ref(false)

const mapId = computed(() => `nearby-connections-${props.hub.uuid}`)

const mapCenter = computed<[number, number]>(() => {
  const points: [number, number][] = []

  if (props.hub.latitude && props.hub.longitude) {
    points.push([props.hub.longitude, props.hub.latitude])
  }
  if (props.railHub.latitude && props.railHub.longitude) {
    points.push([props.railHub.longitude, props.railHub.latitude])
  }

  const allEdges = [...props.autoEdges, ...props.railEdges]
  allEdges.forEach((edge) => {
    if (edge.toLatitude && edge.toLongitude) {
      points.push([edge.toLongitude, edge.toLatitude])
    }
  })

  if (points.length === 0) return [0, 0]

  const avgLng = points.reduce((sum, coord) => sum + coord[0], 0) / points.length
  const avgLat = points.reduce((sum, coord) => sum + coord[1], 0) / points.length

  return [avgLng, avgLat]
})

const mapZoom = computed(() => {
  const distances = [...props.autoEdges, ...props.railEdges].map(e => e.distanceKm || 0)
  if (distances.length === 0) return 5

  const maxDistance = Math.max(...distances)
  if (maxDistance > 2000) return 2
  if (maxDistance > 1000) return 3
  if (maxDistance > 500) return 4
  if (maxDistance > 200) return 5
  return 6
})

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

const buildRouteFeatureCollection = (routes: RouteGeometry[], transportType: 'auto' | 'rail') => ({
  type: 'FeatureCollection' as const,
  features: routes.map(route => ({
    type: 'Feature' as const,
    properties: { toUuid: route.toUuid, transportType },
    geometry: {
      type: 'LineString' as const,
      coordinates: route.coordinates
    }
  }))
})

const buildNeighborsFeatureCollection = (edges: EdgeType[], transportType: 'auto' | 'rail') => ({
  type: 'FeatureCollection' as const,
  features: edges
    .filter(e => e.toLatitude && e.toLongitude)
    .map(edge => ({
      type: 'Feature' as const,
      properties: {
        uuid: edge.toUuid,
        name: edge.toName,
        distanceKm: edge.distanceKm,
        transportType
      },
      geometry: {
        type: 'Point' as const,
        coordinates: [edge.toLongitude!, edge.toLatitude!]
      }
    }))
})

const updateRoutesSource = () => {
  const map = mapRef.value
  if (!map) return

  const autoSource = map.getSource('auto-routes') as mapboxgl.GeoJSONSource | undefined
  if (autoSource) {
    autoSource.setData(buildRouteFeatureCollection(props.autoRouteGeometries, 'auto'))
  }

  const railSource = map.getSource('rail-routes') as mapboxgl.GeoJSONSource | undefined
  if (railSource) {
    railSource.setData(buildRouteFeatureCollection(props.railRouteGeometries, 'rail'))
  }
}

const updateNeighborsSource = () => {
  const map = mapRef.value
  if (!map) return

  const autoSource = map.getSource('auto-neighbors') as mapboxgl.GeoJSONSource | undefined
  if (autoSource) {
    autoSource.setData(buildNeighborsFeatureCollection(props.autoEdges, 'auto'))
  }

  const railSource = map.getSource('rail-neighbors') as mapboxgl.GeoJSONSource | undefined
  if (railSource) {
    railSource.setData(buildNeighborsFeatureCollection(props.railEdges, 'rail'))
  }
}

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

  const coordinates = [
    ...props.autoRouteGeometries.flatMap(route => route.coordinates || []),
    ...props.railRouteGeometries.flatMap(route => route.coordinates || [])
  ]

  if (coordinates.length === 0) return

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

  map.fitBounds(bounds, {
    padding: 40,
    maxZoom: 8
  })

  didFitBounds.value = true
}

const addHubSource = (map: MapboxMapType, id: string, hub: CurrentHub, color: string) => {
  map.addSource(id, {
    type: 'geojson',
    data: {
      type: 'Feature',
      properties: { name: hub.name },
      geometry: {
        type: 'Point',
        coordinates: [hub.longitude, hub.latitude]
      }
    }
  })

  map.addLayer({
    id: `${id}-circle`,
    type: 'circle',
    source: id,
    paint: {
      'circle-radius': 10,
      'circle-color': color,
      'circle-stroke-width': 2,
      'circle-stroke-color': '#ffffff'
    }
  })

  map.on('click', `${id}-circle`, (e) => {
    const feature = e.features?.[0]
    if (!feature) return
    const coordinates = (feature.geometry as GeoJSON.Point).coordinates.slice() as [number, number]
    const name = feature.properties?.name

    new Popup()
      .setLngLat(coordinates)
      .setHTML(`<strong>${name}</strong>`)
      .addTo(map)
  })

  map.on('mouseenter', `${id}-circle`, () => { map.getCanvas().style.cursor = 'pointer' })
  map.on('mouseleave', `${id}-circle`, () => { map.getCanvas().style.cursor = '' })
}

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

  const initMap = () => {
    map.addSource('auto-routes', {
      type: 'geojson',
      data: buildRouteFeatureCollection(props.autoRouteGeometries, 'auto')
    })

    map.addSource('rail-routes', {
      type: 'geojson',
      data: buildRouteFeatureCollection(props.railRouteGeometries, 'rail')
    })

    map.addLayer({
      id: 'auto-routes-lines',
      type: 'line',
      source: 'auto-routes',
      layout: {
        'line-join': 'round',
        'line-cap': 'round'
      },
      paint: {
        'line-color': '#3b82f6',
        'line-width': 4,
        'line-opacity': 0.8
      }
    })

    map.addLayer({
      id: 'rail-routes-lines',
      type: 'line',
      source: 'rail-routes',
      layout: {
        'line-join': 'round',
        'line-cap': 'round'
      },
      paint: {
        'line-color': '#10b981',
        'line-width': 4,
        'line-opacity': 0.8
      }
    })

    addHubSource(map, 'hub-origin', props.hub, '#3b82f6')

    if (props.railHub.uuid && props.railHub.uuid !== props.hub.uuid) {
      addHubSource(map, 'rail-origin', props.railHub, '#10b981')
    }

    map.addSource('auto-neighbors', {
      type: 'geojson',
      data: buildNeighborsFeatureCollection(props.autoEdges, 'auto')
    })

    map.addSource('rail-neighbors', {
      type: 'geojson',
      data: buildNeighborsFeatureCollection(props.railEdges, 'rail')
    })

    map.addLayer({
      id: 'auto-neighbors-circles',
      type: 'circle',
      source: 'auto-neighbors',
      paint: {
        'circle-radius': 8,
        'circle-color': '#ef4444',
        'circle-stroke-width': 2,
        'circle-stroke-color': '#ffffff'
      }
    })

    map.addLayer({
      id: 'rail-neighbors-circles',
      type: 'circle',
      source: 'rail-neighbors',
      paint: {
        'circle-radius': 8,
        'circle-color': '#22c55e',
        'circle-stroke-width': 2,
        'circle-stroke-color': '#ffffff'
      }
    })

    const onNeighborsClick = (e: mapboxgl.MapLayerMouseEvent) => {
      const feature = e.features?.[0]
      if (!feature) return
      const coordinates = (feature.geometry as GeoJSON.Point).coordinates.slice() as [number, number]
      const featureProps = feature.properties
      const name = featureProps?.name
      const distanceKm = featureProps?.distanceKm

      new Popup()
        .setLngLat(coordinates)
        .setHTML(`<strong>${name}</strong><br/>${distanceKm} km`)
        .addTo(map)
    }

    map.on('click', 'auto-neighbors-circles', onNeighborsClick)
    map.on('click', 'rail-neighbors-circles', onNeighborsClick)

    map.on('mouseenter', 'auto-neighbors-circles', () => { map.getCanvas().style.cursor = 'pointer' })
    map.on('mouseleave', 'auto-neighbors-circles', () => { map.getCanvas().style.cursor = '' })
    map.on('mouseenter', 'rail-neighbors-circles', () => { map.getCanvas().style.cursor = 'pointer' })
    map.on('mouseleave', 'rail-neighbors-circles', () => { map.getCanvas().style.cursor = '' })

    isMapReady.value = true
    updateRoutesSource()
    updateNeighborsSource()
    fitMapToRoutes()
  }

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

watch(
  () => [props.autoRouteGeometries, props.railRouteGeometries],
  () => {
    didFitBounds.value = false
    updateRoutesSource()
    if (isMapReady.value) {
      fitMapToRoutes()
    }
  },
  { deep: true }
)

watch(
  () => [props.autoEdges, props.railEdges],
  () => updateNeighborsSource(),
  { deep: true }
)
</script>
