<template>
  <div class="map-container">
    <l-map
      :zoom="mergedOptions.zoom"
      :style="`height: ${mergedOptions.height}; width: ${mergedOptions.width}`"
      class="leaflet-map"
      :center="[lat, lon]"
      @click="clickMap"
      @update:zoom="updateZoom"
      :options="{ zoomControl: false }"
      ref="mapComponent"
      @ready="onMapReady"
    >
      <l-tile-layer :url="tiles?.endpoint" :attribution="attribution">
      </l-tile-layer>
      <l-control-zoom
        position="topleft"
        :zoomInTitle="$t('Zoom in')"
        :zoomOutTitle="$t('Zoom out')"
      ></l-control-zoom>
      <l-marker
        :lat-lng="[lat, lon]"
        @add="openPopup"
        @update:latLng="updateDraggableMarkerPosition"
        :draggable="!readOnly"
      >
        <l-icon>
          <MapMarker :size="48" class="text-mbz-purple" />
        </l-icon>
        <l-popup v-if="popupMultiLine" :options="{ offset: new Point(22, 8) }">
          <span v-for="line in popupMultiLine" :key="line"
            >{{ line }}<br
          /></span>
        </l-popup>
      </l-marker>
    </l-map>
    <CrosshairsGps ref="locationIcon" class="hidden" />
  </div>
</template>

<script lang="ts" setup>
import {
  LatLng,
  LeafletMouseEvent,
  LeafletEvent,
  Control,
  DomUtil,
  Map,
  Point,
} from "leaflet";
import "leaflet/dist/leaflet.css";
import {
  LMap,
  LTileLayer,
  LMarker,
  LPopup,
  LIcon,
  LControlZoom,
} from "@vue-leaflet/vue-leaflet";
import { computed, nextTick, onMounted, ref } from "vue";
import { useMapTiles } from "@/composition/apollo/config";
import { useI18n } from "vue-i18n";
import Locatecontrol from "leaflet.locatecontrol";
import CrosshairsGps from "vue-material-design-icons/CrosshairsGps.vue";
import MapMarker from "vue-material-design-icons/MapMarker.vue";

const props = withDefaults(
  defineProps<{
    readOnly?: boolean;
    coords: string;
    marker?: { text: string | string[]; icon: string };
    options?: Record<string, unknown>;
    updateDraggableMarkerCallback?: (latlng: LatLng, zoom: number) => void;
  }>(),
  {
    readOnly: true,
  }
);

const defaultOptions: {
  zoom: number;
  height: string;
  width: string;
} = {
  zoom: 15,
  height: "100%",
  width: "100%",
};

const zoom = ref(defaultOptions.zoom);

const mapComponent = ref();
const mapObject = ref<Map>();
const locateControl = ref<Control.Locate>();
const locationIcon = ref();

const locationIconHTML = computed(() => locationIcon.value?.$el.innerHTML);

onMounted(async () => {
  // this part resolve an issue where the markers would not appear
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  // eslint-disable-next-line no-underscore-dangle
  // delete Icon.Default.prototype._getIconUrl;
  // Icon.Default.mergeOptions({
  //   iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
  //   iconUrl: require("leaflet/dist/images/marker-icon.png"),
  //   shadowUrl: require("leaflet/dist/images/marker-shadow.png"),
  // });
});

const onMapReady = async () => {
  mapObject.value = mapComponent.value.leafletObject;
  mountLocateControl();
};

const mountLocateControl = () => {
  if (canDoGeoLocation.value && mapObject.value) {
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    locateControl.value = new Locatecontrol({
      strings: { title: t("Show me where I am") as string },
      position: "topleft",
      drawCircle: false,
      drawMarker: false,
      createButtonCallback(container: HTMLElement | undefined, options: any) {
        const link = DomUtil.create(
          "a",
          "leaflet-bar-part leaflet-bar-part-single",
          container
        );
        link.title = options.strings.title;
        link.href = "#";
        link.setAttribute("role", "button");

        const icon = DomUtil.create(
          "span",
          "material-design-icon rss-icon",
          link
        );
        icon.setAttribute("aria-hidden", "true");
        icon.setAttribute("role", "img");
        icon.insertAdjacentHTML("beforeend", locationIconHTML.value);
        return { link, icon };
      },
      ...props.options,
    }) as Control.Locate;
    locateControl.value?.addTo(mapObject.value);
  }
};

const openPopup = async (event: LeafletEvent): Promise<void> => {
  await nextTick();
  event.target.openPopup();
};

const mergedOptions = computed((): Record<string, unknown> => {
  return { ...defaultOptions, ...props.options };
});

const lat = computed((): number => {
  return Number.parseFloat(props.coords?.split(";")[1]);
});

const lon = computed((): number => {
  return Number.parseFloat(props.coords.split(";")[0]);
});

const popupMultiLine = computed((): Array<string | undefined> => {
  if (Array.isArray(props.marker?.text)) {
    return props.marker?.text as string[];
  }
  return [props.marker?.text];
});

const clickMap = (event: LeafletMouseEvent): void => {
  updateDraggableMarkerPosition(event.latlng);
};

const updateDraggableMarkerPosition = (e: LatLng): void => {
  if (props.updateDraggableMarkerCallback) {
    props.updateDraggableMarkerCallback(e, zoom.value);
  }
};

const updateZoom = (newZoom: number): void => {
  zoom.value = newZoom;
};

const { tiles } = useMapTiles();

const { t } = useI18n({ useScope: "global" });

const attribution = computed((): string => {
  return tiles.value?.attribution ?? t("© The OpenStreetMap Contributors");
});

const canDoGeoLocation = computed((): boolean => {
  return window.isSecureContext;
});
</script>
<style lang="scss" scoped>
div.map-container {
  height: 100%;
  width: 100%;

  .leaflet-map {
    z-index: 20;
  }
}
</style>
<style>
@import "leaflet.locatecontrol/dist/L.Control.Locate.css";
</style>
