(function () {
    const lumajangCenter = [-8.1269, 113.2248];
    const lumajangBounds = L.latLngBounds(
        L.latLng(-8.55, 112.75),
        L.latLng(-7.70, 113.55),
    );
    const defaultZoom = 10;
    const singleMarkerZoom = 14;
    const minZoom = 10;
    const maxZoom = 18;
    const markerColors = {
        ar: '#dc2626',
        video: '#2563eb',
    };

    let map;
    let markerLayer;
    let markers = [];
    let activeMedia = 'all';
    let searchQuery = '';

    function escapeHtml(value) {
        return String(value ?? '')
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
    }

    function createMarkerIcon(mediaType) {
        const color = markerColors[mediaType] || markerColors.ar;

        return L.divIcon({
            className: 'tourism-marker',
            html: `<span class="tourism-marker-pin" style="--marker-color:${color}"></span>`,
            iconSize: [30, 42],
            iconAnchor: [15, 42],
            popupAnchor: [0, -38],
        });
    }

    function buildPopupContent(item) {
        const isVideo = item.mediaType === 'video';
        const badgeClass = isVideo ? 'text-bg-primary' : 'text-bg-danger';

        return `
            <div class="card map-popup-card border-0 shadow-sm">
                <img src="${escapeHtml(item.imageUrl)}" class="card-img-top" alt="${escapeHtml(item.title)}">
                <div class="card-body">
                    <div class="d-flex align-items-start justify-content-between gap-2 mb-2">
                        <h3 class="map-popup-title mb-0">${escapeHtml(item.title)}</h3>
                        <span class="badge ${badgeClass}">${escapeHtml(item.mediaLabel)}</span>
                    </div>
                    <div class="map-popup-row"><i class="bi bi-geo-alt"></i><span>${escapeHtml(item.location)}</span></div>
                    <div class="map-popup-row"><i class="bi bi-tag"></i><span>${escapeHtml(item.category)}</span></div>
                    <a class="btn btn-sm btn-ocean w-100 mt-3" href="${escapeHtml(item.editUrl)}">Lihat Detail</a>
                </div>
            </div>
        `;
    }

    function markerMatches(marker) {
        const item = marker.tourismData;
        const matchesMedia = activeMedia === 'all' || item.mediaType === activeMedia;
        const haystack = `${item.title} ${item.location} ${item.category}`.toLowerCase();
        const matchesSearch = !searchQuery || haystack.includes(searchQuery);

        return matchesMedia && matchesSearch;
    }

    function getVisibleMarkers() {
        return markers.filter((marker) => markerLayer.hasLayer(marker));
    }

    function setMarkerVisibility() {
        markers.forEach((marker) => {
            const shouldShow = markerMatches(marker);
            const isShown = markerLayer.hasLayer(marker);

            if (shouldShow && !isShown) {
                markerLayer.addLayer(marker);
            }

            if (!shouldShow && isShown) {
                markerLayer.removeLayer(marker);
            }
        });

        const visibleMarkers = getVisibleMarkers();
        updateStats();

        if (!visibleMarkers.length) {
            map.closePopup();
            return;
        }

        const exactSearchMatch = searchQuery
            ? visibleMarkers.find((marker) => marker.tourismData.title.toLowerCase().includes(searchQuery))
            : null;

        if (exactSearchMatch) {
            focusMarker(exactSearchMatch);
            return;
        }

        fitMarkers(visibleMarkers);
    }

    function updateStats() {
        const totalElement = document.getElementById('mapTotalMarkers');
        const arElement = document.getElementById('mapArMarkers');
        const videoElement = document.getElementById('mapVideoMarkers');

        const arCount = markers.filter((marker) => marker.tourismData.mediaType === 'ar').length;
        const videoCount = markers.filter((marker) => marker.tourismData.mediaType === 'video').length;

        if (totalElement) totalElement.textContent = markers.length;
        if (arElement) arElement.textContent = arCount;
        if (videoElement) videoElement.textContent = videoCount;
    }

    function fitMarkers(targetMarkers) {
        if (!targetMarkers.length) {
            map.setView(lumajangCenter, defaultZoom);
            return;
        }

        if (targetMarkers.length === 1) {
            map.setView(targetMarkers[0].getLatLng(), singleMarkerZoom);
            return;
        }

        const group = L.featureGroup(targetMarkers);

        map.fitBounds(group.getBounds().pad(0.2), {
            maxZoom,
        });
    }

    function refreshMapSize(callback, delay = 140) {
        window.setTimeout(() => {
            if (!map) {
                return;
            }

            map.invalidateSize(true);

            if (typeof callback === 'function') {
                callback();
            }
        }, delay);
    }

    function scheduleMapInvalidation() {
        setTimeout(() => {
            map.invalidateSize(true);
        }, 500);

        setTimeout(() => {
            map.invalidateSize(true);
        }, 1000);

        setTimeout(() => {
            map.invalidateSize(true);
        }, 2000);
    }

    function focusMarker(marker) {
        map.setView(marker.getLatLng(), singleMarkerZoom);
        marker.openPopup();
    }

    function resetView() {
        searchQuery = '';
        activeMedia = 'all';

        const searchInput = document.getElementById('tourismMapSearch');
        const mediaSelect = document.getElementById('tourismMapMedia');

        if (searchInput) searchInput.value = '';
        if (mediaSelect) mediaSelect.value = 'all';

        markers.forEach((marker) => {
            if (!markerLayer.hasLayer(marker)) {
                markerLayer.addLayer(marker);
            }
        });

        map.closePopup();
        updateStats();
        refreshMapSize(() => map.setView(lumajangCenter, defaultZoom), 120);
    }

    function addFullscreenControl() {
        const FullscreenControl = L.Control.extend({
            options: { position: 'topleft' },
            onAdd() {
                const button = L.DomUtil.create('button', 'leaflet-control fullscreen-control');
                button.type = 'button';
                button.title = 'Fullscreen';
                button.setAttribute('aria-label', 'Fullscreen');
                button.innerHTML = '<i class="bi bi-fullscreen"></i>';

                L.DomEvent.disableClickPropagation(button);
                L.DomEvent.on(button, 'click', () => {
                    const container = map.getContainer();

                    if (document.fullscreenElement) {
                        document.exitFullscreen();
                    } else if (container.requestFullscreen) {
                        container.requestFullscreen();
                    }
                });

                return button;
            },
        });

        map.addControl(new FullscreenControl());

        document.addEventListener('fullscreenchange', () => {
            refreshMapSize(() => fitMarkers(getVisibleMarkers()), 180);
        });
    }

    function bindControls() {
        document.getElementById('tourismMapSearch')?.addEventListener('input', (event) => {
            searchQuery = event.target.value.trim().toLowerCase();
            setMarkerVisibility();
        });

        document.getElementById('tourismMapMedia')?.addEventListener('change', (event) => {
            activeMedia = event.target.value;
            setMarkerVisibility();
        });

        document.getElementById('tourismMapFit')?.addEventListener('click', () => {
            fitMarkers(getVisibleMarkers());
        });

        document.getElementById('tourismMapReset')?.addEventListener('click', resetView);

        window.addEventListener('resize', () => {
            window.clearTimeout(window.tourismMapResizeTimer);
            window.tourismMapResizeTimer = window.setTimeout(() => {
                refreshMapSize(() => fitMarkers(getVisibleMarkers()), 40);
            }, 180);
        });

        window.addEventListener('load', () => {
            refreshMapSize(() => fitMarkers(getVisibleMarkers()), 220);
        });
    }

    function initTourismMap() {
        const mapElement = document.getElementById('tourismMap');
        const data = Array.isArray(window.tourismMapData) ? window.tourismMapData : [];

        if (!mapElement || typeof L === 'undefined') {
            return;
        }

        if (mapElement._leaflet_id) return;

        map = L.map(mapElement, {
            center: lumajangCenter,
            zoom: defaultZoom,
            minZoom,
            maxZoom,
            maxBounds: lumajangBounds,
            maxBoundsViscosity: 1,
            zoomControl: true,
            scrollWheelZoom: true,
        });

        map.setMaxBounds(lumajangBounds);

        L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
            subdomains: 'abcd',
            maxZoom: 20,
            noWrap: true,
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
        }).addTo(map);

        scheduleMapInvalidation();

        markerLayer = L.layerGroup().addTo(map);

        markers = data.map((item) => {
            const marker = L.marker([Number(item.latitude), Number(item.longitude)], {
                icon: createMarkerIcon(item.mediaType),
                title: item.title,
            });

            marker.tourismData = item;
            marker.bindPopup(buildPopupContent(item), {
                className: 'tourism-popup',
                maxWidth: 300,
                minWidth: 260,
            });
            markerLayer.addLayer(marker);

            return marker;
        });

        addFullscreenControl();
        bindControls();
        updateStats();

        if (markers.length) {
            refreshMapSize(() => map.setView(lumajangCenter, defaultZoom), 180);
        } else {
            mapElement.classList.add('is-empty');
            refreshMapSize(() => map.setView(lumajangCenter, defaultZoom), 180);
        }
    }

    function bootTourismMap() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initTourismMap, { once: true });
        } else {
            initTourismMap();
        }
    }

    bootTourismMap();
})();
