import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useCallback, useContext, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useI18n, useTheme } from '../../hooks';
import { createUID } from '../../utils';
import Progress from '../Progress';
import { StyledLocationError, StyledLocationView, StyledMap } from './LocationView.styles';
import MapsContext from './MapsContext';
import { getAddress, getPlaceById, isLatLngObject, loadMapsAPI, toGoogleLatLng } from './utils';
var LoadStatus;
(function (LoadStatus) {
    LoadStatus["Init"] = "init";
    LoadStatus["Ready"] = "ready";
    LoadStatus["Error"] = "error";
})(LoadStatus || (LoadStatus = {}));
const hasPlaceId = (obj) => {
    return (typeof obj === 'object' &&
        !!obj &&
        'placeId' in obj &&
        !!obj.placeId &&
        typeof obj.placeId === 'string');
};
/* cSpell:ignore 'ABCDEFGHIJKLMNOPQRSTUVWYZ' */
const markerLabels = 'ABCDEFGHIJKLMNOPQRSTUVWYZ';
const LocationView = forwardRef(function LocationView(props, ref) {
    const { pins = [], zoomLevel = 13, height = '25rem', centerMapOnChange = false, onClick, onError, disabled = false, loading = false, drawRoute = false, ...restProps } = props;
    const t = useI18n();
    const { name, ...providerOpts } = useContext(MapsContext);
    const [status, setStatus] = useState(LoadStatus.Init);
    const [eMessage, setEMessage] = useState('');
    const [selectedPinIndex, setSelectedPinIndex] = useState(undefined);
    const mapElemRef = useRef();
    const map = useRef();
    const markers = useRef([]);
    const ds = useRef();
    const dr = useRef();
    const infoWindowContent = useRef(document.createElement('div'));
    const theme = useTheme();
    const onMapClick = (e) => {
        if (!isLatLngObject(e.latLng))
            return;
        if (hasPlaceId(e)) {
            getPlaceById(e.placeId).then(placeInfo => {
                onClick?.({
                    ...placeInfo,
                    latitude: e.latLng.lat(),
                    longitude: e.latLng.lng()
                });
            });
        }
        else {
            getAddress({
                latitude: e.latLng.lat(),
                longitude: e.latLng.lng()
            })
                .then(onClick)
                .catch(err => onError?.(err));
        }
    };
    const centerMap = useCallback((coords) => {
        if (!map.current || coords.length === 0)
            return;
        if (coords.length === 1) {
            map.current.setCenter(toGoogleLatLng(coords[0]));
            map.current.setZoom(zoomLevel);
        }
        else {
            const bounds = new google.maps.LatLngBounds();
            coords.forEach(({ latitude, longitude }) => {
                bounds.extend(toGoogleLatLng({ latitude, longitude }));
            });
            map.current.fitBounds(bounds);
        }
    }, []);
    const initialize = () => {
        if (mapElemRef.current) {
            const position = toGoogleLatLng({ latitude: 0, longitude: 0 });
            map.current = new google.maps.Map(mapElemRef.current, {
                mapId: createUID(),
                center: position,
                zoom: 2,
                draggable: !disabled,
                draggableCursor: 'auto',
                draggingCursor: 'move'
            });
            ds.current = new google.maps.DirectionsService();
            dr.current = new google.maps.DirectionsRenderer({ suppressMarkers: true });
            centerMap(pins);
        }
    };
    useEffect(() => {
        if (!onClick || !map.current)
            return;
        const l = map.current.addListener('click', onMapClick);
        return () => google.maps.event.removeListener(l);
    }, [onClick, map.current]);
    useEffect(() => {
        let mounted = true;
        loadMapsAPI(name, providerOpts)
            .then(() => {
            if (mounted) {
                initialize();
                setStatus(LoadStatus.Ready);
            }
        })
            .catch((error) => {
            setStatus(LoadStatus.Error);
            setEMessage(error.message);
            onError?.(error);
        });
        return () => {
            mounted = false;
            if (map.current)
                google.maps.event.clearInstanceListeners(map.current);
        };
    }, []);
    useEffect(() => {
        if (status !== LoadStatus.Ready ||
            !map.current ||
            !dr.current ||
            !ds.current ||
            // FIXME: Workaround for marker library not being imported
            !google.maps.marker)
            return;
        if (drawRoute && pins.length > 1) {
            dr.current.setMap(map.current);
            const request = {
                origin: toGoogleLatLng({ latitude: pins[0].latitude, longitude: pins[0].longitude }),
                destination: toGoogleLatLng({
                    latitude: pins[pins.length - 1].latitude,
                    longitude: pins[pins.length - 1].longitude
                }),
                waypoints: pins.slice(1, pins.length - 1).map(l => {
                    return {
                        stopover: true,
                        location: toGoogleLatLng({
                            latitude: l.latitude,
                            longitude: l.longitude
                        })
                    };
                }),
                travelMode: google.maps.TravelMode.DRIVING
            };
            ds.current.route(request)?.then(dr.current.setDirections.bind(dr.current), onError);
        }
        const infoWindow = new google.maps.InfoWindow();
        try {
            markers.current = pins.map(({ latitude, longitude, title, content, selected }, index) => {
                const gCoords = toGoogleLatLng({ latitude, longitude });
                const contentConfig = {
                    glyphColor: theme.base.colors.white
                };
                const label = markerLabels[index % markerLabels.length];
                if (pins.length > 1) {
                    const glyphWrapper = document.createElement('span');
                    glyphWrapper.style.fontSize = '0.9rem';
                    glyphWrapper.style.paddingTop = '0.1rem';
                    glyphWrapper.innerHTML = label;
                    contentConfig.glyph = glyphWrapper;
                }
                if (selected) {
                    contentConfig.background = theme.base.colors.red.dark;
                    contentConfig.borderColor = theme.base.colors.red.dark;
                    contentConfig.scale = 1.15;
                }
                const pin = new google.maps.marker.PinElement(contentConfig);
                const marker = new google.maps.marker.AdvancedMarkerElement({
                    map: map.current,
                    position: gCoords,
                    title,
                    content: pin.element
                });
                if (content) {
                    marker.addListener('click', () => {
                        setSelectedPinIndex(index);
                        infoWindow.setOptions({
                            ariaLabel: `${t('location_info')} - ${title ?? label}`,
                            headerContent: title ?? label
                        });
                        infoWindow.setContent(infoWindowContent.current);
                        infoWindow.open({
                            anchor: marker,
                            map: map.current
                        });
                    });
                }
                return marker;
            });
        }
        catch {
            markers.current = [];
        }
        if (centerMapOnChange) {
            centerMap(pins);
        }
        return () => {
            markers.current.forEach(m => {
                m.map = null;
            });
            dr.current?.setMap(null);
        };
    }, [
        JSON.stringify(pins, ['title', 'selected', 'latitude', 'longitude']),
        status,
        zoomLevel,
        disabled,
        drawRoute,
        centerMapOnChange,
        onError
    ]);
    return (_jsxs(StyledLocationView, { "$height": height, ref: ref, children: [_jsx(StyledMap, { ref: mapElemRef, disabled: disabled, status: status, ...restProps }), (status === LoadStatus.Init || loading) && _jsx(Progress, { placement: 'local' }), status === LoadStatus.Error && eMessage && (_jsx(StyledLocationError, { "aria-label": eMessage, variant: 'info', children: eMessage })), selectedPinIndex !== undefined && !!pins[selectedPinIndex].content && (_jsx(_Fragment, { children: createPortal(pins[selectedPinIndex].content, infoWindowContent.current) }))] }));
});
export default LocationView;
//# sourceMappingURL=LocationView.js.map