<template>
    <div class="tb-map-renderer">
        <div class="tb-map-renderer__map-container" ref="map" />

        <div
            class="tb-map-renderer__info-window-rendering-helper"
            ref="infoWindowRenderer"
            v-if="markerForInfoWindow"
        >
            <slot name="info-window" :marker="markerForInfoWindow" />
        </div>
    </div>
</template>
<script>
import { initializeMap } from './map-factory';
import { notifyErrorIfInDevelopment } from '../../common/development-error-notification';

export default {
    name: 'TbMapRenderer',

    props: {
        mapType: { type: String, required: true },
        settings: { type: Object, required: true },
        markers: { type: Array, required: true },
    },

    data: () => ({
        map: null,
        markerForInfoWindow: null,
        infoWindowContentNode: null,
    }),

    async mounted() {
        this.map = await initializeMap(this.mapType, this.$refs.map, this.settings);
        this.$watch('markers', markers => this.render(markers), { immediate: true });
        this.map.onMarkerClick(marker => this.onMarkerClick(marker));
    },

    watch: {
        markers() {
            this.closeInfoWindow();
        },
    },

    methods: {
        invalidateSize() {
            if (
                this.map &&
                this.map.map &&
                this.map.map.invalidateSize
            ) {
                // https://leafletjs.com/reference-1.6.0.html#map-invalidatesize
                this.map.map.invalidateSize();
            }
        },

        closeInfoWindow() {
            // Note: This method is called from the outside component.
            if (this.map && this.map.closeInfoWindow) {
                this.map.closeInfoWindow();
            }
        },

        async centerMapOnMarkers() {
            // Note: This method is called from the outside component.
            setTimeout(() => {
                if (this.map && this.map.centerMapOnMarkers) {
                    this.map.centerMapOnMarkers(this.markers);
                }
            }, 250);
        },

        async render(markers) {
            await this.map.render(markers);
            this.paintAlwaysShownToursOntoMap(markers);
        },

        async onMarkerClick(marker) {
            if (this.map.clearTours) {
                this.map.clearTours();
                this.paintAlwaysShownToursOntoMap(this.markers);
            }


            this.markerForInfoWindow = marker;
            await this.$nextTick();
            this.fetchDetailData(marker).then(() => {
                if (
                    marker.tour
                    && marker.tour.polygon
                    && marker.tour.polygon.length > 0
                    && this.map.drawTourOntoMap
                ) {
                    this.map.drawTourOntoMap(marker, false);
                }
            });

            this.infoWindowContentNode = this.$refs.infoWindowRenderer.firstChild || this.infoWindowContentNode;
            this.map.openInfoWindow(marker, this.infoWindowContentNode);
        },

        /**
         * @private
         * @param {Marker[]} markers
         */
        paintAlwaysShownToursOntoMap(markers) {
            if (!this.map.drawTourOntoMap) {
                return;
            }

            if (this.map.clearTours && this.map.drawTourOntoMap) {
                this.map.clearTours();
                markers.filter(marker => marker.tour && marker.tour.alwaysDisplayPolygon)
                    .forEach(marker => this.paintAlwaysShownTourOntoMap(marker));
            }
        },

        /**
         * @private
         * @param {Marker} marker
         */
        async paintAlwaysShownTourOntoMap(marker) {
            if (marker.tour && !marker.tour.polygon && marker.detailApiEndpoints['tour.polygon']) {
                await this.fetchApiEndpoint(marker.detailApiEndpoints['tour.polygon'], marker, 'tour.polygon');
            }
            this.map.drawTourOntoMap(marker, false)
        },

        fetchDetailData(marker) {
            const promises = [];

            for (const key of Object.keys(marker.detailApiEndpoints)) {
                if (marker.detail[key] && !(marker.detail[key] instanceof Array && marker.detail[key].length === 0)) {
                    continue;
                }

                promises.push(this.fetchApiEndpoint(marker.detailApiEndpoints[key], marker, key).catch(err => {
                    notifyErrorIfInDevelopment(err);
                    console.error(err);
                }));
            }

            return Promise.all(promises);
        },

        async fetchApiEndpoint(apiEndpointUrl, marker, attributeName) {
            const response = await fetch(apiEndpointUrl);
            const data = await response.json();
            if (response.status !== 200) {
                if (data.error) {
                    console.error(data.error.message);
                    // eslint-disable-next-line no-console
                    console.table(data.trace);
                }
                throw data;
            }

            if (attributeName === 'tour.polygon' && marker.tour) {
                this.$set(marker.tour, 'polygon', data);
            }

            this.$set(marker.detail, attributeName, data);
        },
    },
};
</script>
