<script setup lang="ts">
import type { LatLngExpression, Map, Marker } from 'leaflet'
import { appendScript, sleep } from '@bagelink/vue'
import { onMounted, ref, watch } from 'vue'
import './leaflet.css'

type MapMarker = {
	lat: number | string
	lng: number | string
	tooltip?: string
	popup?: string
	title?: string
	[key: string]: any
} | [(number | string), (number | string), string?]

const props = withDefaults(
	defineProps<{
		center?: LatLngExpression
		zoom?: number
		height?: number
		zoomControl?: boolean
		markers?: MapMarker[]
		markerIcon?: string
	}>(),
	{
		center: () => [31.7683, 35.2137],
		zoom: 15,
		height: 400,
		zoomControl: true,
		markerIcon: '',
	}
)

const emit = defineEmits(['select'])
const L = ref<any>()
const map = ref<Map>()
const _markers = ref<Marker[]>([])
const id = ref(Math.random().toString(36).slice(2, 10))

const defaultMarkerSVG = '<svg width="28" height="38" viewBox="0 0 28 38" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.2263 37.7955C17.0897 37.7955 19.4109 37.0138 19.4109 36.0496C19.4109 35.0854 17.0897 34.3037 14.2263 34.3037C11.363 34.3037 9.04175 35.0854 9.04175 36.0496C9.04175 37.0138 11.363 37.7955 14.2263 37.7955Z" fill="black" fill-opacity="0.1"/><path d="M14.2265 0.549591C21.2842 0.549591 27.0131 6.23786 27.0787 13.28V13.4024C27.0787 19.3328 24.4759 24.4306 21.5627 28.2764C18.6511 32.12 15.4577 34.6754 14.3457 35.5097C14.2748 35.5629 14.1778 35.5629 14.1068 35.5097C12.9947 34.675 9.80135 32.1197 6.88984 28.2762C3.97665 24.4304 1.37378 19.3328 1.37378 13.4024C1.37378 6.30387 7.12806 0.549591 14.2265 0.549591Z" fill="#ED1b3E" stroke="#ED6C6F"/><path d="M14.2263 21.6185C18.7639 21.6185 22.4424 17.94 22.4424 13.4024C22.4424 8.86477 18.7639 5.18631 14.2263 5.18631C9.68872 5.18631 6.01025 8.86477 6.01025 13.4024C6.01025 17.94 9.68872 21.6185 14.2263 21.6185Z" fill="white"/></svg>'
const leafletScriptUrl = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'

async function loadGlobalL() {
	while (!window.L) { await sleep(100) }
	return window.L
}

async function initializeMap() {
	await appendScript(leafletScriptUrl)
	L.value = await loadGlobalL()
	if (!map.value) {
		map.value = L.value.map(id.value, {
			center: props.center,
			zoom: props.zoom,
			zoomControl: props.zoomControl,
		})

		L.value.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, }).addTo(map.value)
	}
}

async function addMarker(marker: MapMarker) {
	const iconSVG = props.markerIcon || defaultMarkerSVG
	const icon = L.value?.icon({
		iconUrl: `data:image/svg+xml;utf8,${encodeURIComponent(iconSVG)}`,
		iconSize: [32, 32],
	})
	const latlng = latLangFromMarker(marker)
	const title = Array.isArray(marker) ? marker[2] : marker.title ?? marker.tooltip
	const tooltip = Array.isArray(marker) ? marker[2] : marker.tooltip
	const m = L.value?.marker(latlng, { title, icon, interactive: true }).addTo(map.value as Map) as Marker
	if (tooltip) { m.bindTooltip(tooltip, { direction: 'top' }) } // Use `bindPopup` if you want a click-to-show label
	m.on('click', () => { emit('select', marker) })

	_markers.value.push(m)
}

function fitMarkers(L: any) {
	if (_markers.value.length > 0) {
		const latLngs = _markers.value.map(marker => marker.getLatLng())
		const bounds = L.latLngBounds(latLngs)
		const center = bounds.getCenter()
		map.value?.setView(center, props.zoom)
		map.value?.fitBounds(bounds, {
			animate: true,
			padding: [20, 20], // Adjust padding as needed
		})
	}
}

function latLangFromMarker(marker: MapMarker): LatLngExpression {
	const [lat, lng] = (Array.isArray(marker)
		? [marker[0], marker[1]]
		: [marker.lat, marker.lng])
	return { lat: +lat, lng: +lng }
}

async function watchMarkers(markers?: MapMarker[]) {
	if (!L.value) { L.value = await loadGlobalL() }
	_markers.value.forEach(marker => marker.remove())
	if (!markers) { return }

	for (const marker of markers) {
		if (!map.value) { await initializeMap() }
		addMarker(marker)
	}
	fitMarkers(L.value)
}

watch(() => props.markers, watchMarkers, { immediate: true })
watch(() => props.center, center => map.value?.setView(center, props.zoom), { immediate: true })

onMounted(initializeMap)
</script>

<template>
	<div :id="id" class="leaflet-map" :style="{ height: `${props.height || 400}px` }" dir="ltr" />
</template>

<style>
.leaflet-map {
  height: 100%;
  border-radius: var(--input-border-radius);
  position: relative;
  background: var(--bgl-bg);
  direction: ltr;
}

.leaflet-map::after {
  content: "";
  pointer-events: none;
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 10px #00000012;
  z-index: 402;
}

.leaflet-map .leaflet-pane .leaflet-tile-pane {
  filter: contrast(0.6) brightness(1.25) hue-rotate(12deg);
}

.leaflet-control-attribution {
  display: none;
}

.leaflet-pane.leaflet-marker-pane img {
  /* filter: drop-shadow(0px 8px 3px rgba(0, 0, 0, 0.2)); */
}

.leaflet-touch .leaflet-control-zoom-in,
.leaflet-touch .leaflet-control-zoom-out {
  font-size: var(--input-font-size);
}

.leaflet-bar {
  border-radius: var(--input-border-radius) !important;
}

.leaflet-touch .leaflet-bar a:first-child {
  border-top-left-radius: calc(var(--input-border-radius) - 2px) !important;
  border-top-right-radius: calc(var(--input-border-radius) - 2px) !important;
}

.leaflet-touch .leaflet-bar a:last-child {
  border-bottom-left-radius: calc(var(--input-border-radius) - 2px) !important;
  border-bottom-right-radius: calc(var(--input-border-radius) - 2px) !important;
}

.leaflet-bar a {
  background: var(--bgl-box-bg) !important;
  transition: var(--bgl-transition);
  border-bottom: 1px solid var(--border-color) !important;
  display: flex;
  justify-content: center;
  align-items: center;
  line-height: 1 !important;
  padding-bottom: 2px;
}
.leaflet-bar a:last-child {
	border-bottom: none !important;
}

.leaflet-bar a:hover {
  filter: var(--bgl-hover-filter);
}

.leaflet-bar a:focus {
  filter: var(--bgl-active-filter);
}

.leaflet-marker-icon {
	cursor: pointer;
}
</style>
