<template>
    <div>
        <div id="map" />
    </div>
</template>

<script>
export default {
    name: 'google-map',
    props: {
        addresses: Array,
        zipCode: String
    },
    data() {
        return {
            country: 'USA',
            geocoder: null
        };
    },
    computed: {
        zipCodeAddress() {
            return `${this.zipCode ?? ''} ${this.country}`;
        }
    },
    watch: {
        async addresses(newAddresses) {
            this.createMapWithMarkersForAddresses(newAddresses);
        }
    },
    methods: {
        async getMap(center) {
            const { Map } = await window.google.maps.importLibrary('maps');
            return new Map(document.getElementById('map'), {
                center,
                mapId: 'map_id',
                mapTypeControl: false
            });
        },
        getBounds(locations) {
            const bounds = new window.google.maps.LatLngBounds();
            locations?.forEach((location) => bounds.extend(location));
            return bounds;
        },
        async setGeocoder() {
            if (this.geocoder == null) {
                const { Geocoder } = await window.google.maps.importLibrary('geocoding');
                this.geocoder = new Geocoder();
            }
        },
        async addMarkersToMap(map, positions) {
            const { AdvancedMarkerElement } = await window.google.maps.importLibrary('marker');
            positions?.forEach((position) => new AdvancedMarkerElement({ map, position }));
        },
        async getLocationsFromAddresses(addresses) {
            await this.setGeocoder();

            const geocodeShopAddressPromises = addresses?.map((address) => this.geocoder.geocode({ address })) ?? [];
            const geocodeShopAddressResults = await Promise.all(geocodeShopAddressPromises);
            return geocodeShopAddressResults.map((result) =>
                (result?.results?.length > 0
                    ? result.results[0].geometry?.location
                    : null))
                .filter((loc) => loc != null);
        },
        async getBoundsFromAddress(address) {
            await this.setGeocoder();
            const result = await this.geocoder.geocode({ address });
            return result?.results?.length > 0
                ? result.results[0].geometry?.bounds
                : null;
        },
        async createMapWithMarkersForAddresses(addresses) {
            const shopPositions = await this.getLocationsFromAddresses(addresses);
            const zipBounds = await this.getBoundsFromAddress(this.zipCodeAddress);
            const map = await this.getMap(zipBounds.getCenter());

            await this.addMarkersToMap(map, shopPositions);

            const positionsToDisplay = shopPositions.concat(zipBounds.getNorthEast(), zipBounds.getSouthWest());
            const bounds = this.getBounds(positionsToDisplay);
            map.fitBounds(bounds);
        }
    }
};
</script>

<style lang="scss" scoped>

#map {
    height: 250px;
    width: 100%;
}
</style>
