/**
 * Havenlytics Map Field Handler - Complete with Leaflet & Google Maps Support
 * @version 3.0.6
 */

(function($) {
    'use strict';

    const mapInstances = new Map();
    const fieldToMapMap = new Map();
    let isUpdating = false;
    let skipReverseGeocode = false;
    let reverseGeocodeTimeout = null;

    $(document).ready(function() {
        setTimeout(initAllMaps, 200);
        
        $(document).on('click', '.hvnly__dyamic_metabox_tab__nav a', function() {
            setTimeout(initAllMaps, 400);
        });
    });

    function initAllMaps() {
        $('.hvnly-leaflet-map-container').each(function() { 
            initLeafletMap($(this)); 
        });
        
        $('.hvnly-google-map-container').each(function() { 
            initGoogleMap($(this)); 
        });
        
        setupFieldSync();
        setupAddressAutocomplete();
    }

    function initLeafletMap($container) {
        const mapId = $container.attr('id');
        if (!mapId || mapInstances.has(mapId)) return;
        
        const latFieldName = $container.data('lat-field-name');
        const lngFieldName = $container.data('lng-field-name');
        const addressFieldName = $container.data('address-field-name');
        const tileUrl = $container.data('tile-url') || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
        const tileAttribution = $container.data('tile-attribution') || '© OpenStreetMap contributors';
        const initialLat = parseFloat($container.data('initial-lat')) || 51.505;
        const initialLng = parseFloat($container.data('initial-lng')) || -0.09;
        const defaultZoom = parseInt($container.data('default-zoom')) || 13;
        
        const $latInput = $('[name="' + latFieldName + '"], #' + latFieldName).first();
        const $lngInput = $('[name="' + lngFieldName + '"], #' + lngFieldName).first();
        const $addressInput = $('[name="' + addressFieldName + '"], #' + addressFieldName).first();
        
        const finalLat = ($latInput.val() && !isNaN(parseFloat($latInput.val()))) ? parseFloat($latInput.val()) : initialLat;
        const finalLng = ($lngInput.val() && !isNaN(parseFloat($lngInput.val()))) ? parseFloat($lngInput.val()) : initialLng;
        
        if ($latInput.attr('id')) fieldToMapMap.set($latInput.attr('id'), mapId);
        if ($lngInput.attr('id')) fieldToMapMap.set($lngInput.attr('id'), mapId);
        if ($addressInput.attr('id')) fieldToMapMap.set($addressInput.attr('id'), mapId);
        
        if (typeof L !== 'undefined') {
            createLeafletMap(mapId, $container, $latInput, $lngInput, $addressInput, finalLat, finalLng, defaultZoom, tileUrl, tileAttribution);
        } else {
            let attempts = 0;
            const interval = setInterval(function() {
                attempts++;
                if (typeof L !== 'undefined') {
                    clearInterval(interval);
                    createLeafletMap(mapId, $container, $latInput, $lngInput, $addressInput, finalLat, finalLng, defaultZoom, tileUrl, tileAttribution);
                } else if (attempts >= 50) {
                    clearInterval(interval);
                }
            }, 100);
        }
    }

    function createLeafletMap(mapId, $container, $latInput, $lngInput, $addressInput, initialLat, initialLng, defaultZoom, tileUrl, tileAttribution) {
        $container.empty();
        
        try {
            const map = L.map(mapId).setView([initialLat, initialLng], defaultZoom);
            L.tileLayer(tileUrl, { attribution: tileAttribution }).addTo(map);
            
            const customIcon = L.divIcon({
                className: 'hvnly-fontawesome-marker',
                html: '<i class="fas fa-map-marker-alt" style="font-size: 28px; color: #6C60FE;"></i>',
                iconSize: [28, 28],
                iconAnchor: [14, 28],
                popupAnchor: [0, -28]
            });
            
            const marker = L.marker([initialLat, initialLng], { draggable: true, icon: customIcon }).addTo(map);
            marker.bindPopup(`Location<br>Lat: ${initialLat.toFixed(6)}<br>Lng: ${initialLng.toFixed(6)}`);
            
            const instance = { map, marker, $latInput, $lngInput, $addressInput, $container, mapId, type: 'leaflet' };
            mapInstances.set(mapId, instance);
            
            setupGeocodeButton(instance);
            
            marker.on('dragend', function(e) {
                if (isUpdating) return;
                isUpdating = true;
                const lat = e.target.getLatLng().lat;
                const lng = e.target.getLatLng().lng;
                updateFieldValue($latInput, lat.toFixed(6));
                updateFieldValue($lngInput, lng.toFixed(6));
                updateCoordinatesDisplay($container, lat, lng);
                reverseGeocodeAddress(lat, lng, $addressInput, $container);
                setTimeout(() => { isUpdating = false; }, 100);
            });
            
            map.on('click', function(e) {
                if (isUpdating) return;
                isUpdating = true;
                const lat = e.latlng.lat;
                const lng = e.latlng.lng;
                marker.setLatLng(e.latlng);
                updateFieldValue($latInput, lat.toFixed(6));
                updateFieldValue($lngInput, lng.toFixed(6));
                updateCoordinatesDisplay($container, lat, lng);
                reverseGeocodeAddress(lat, lng, $addressInput, $container);
                setTimeout(() => { isUpdating = false; }, 100);
            });
            
            setTimeout(() => map.invalidateSize(), 200);
            
        } catch (error) {
            $container.html('<div style="padding: 20px; text-align: center; color: red;">Map error</div>');
        }
    }

    function initGoogleMap($container) {
        const mapId = $container.attr('id');
        if (!mapId || mapInstances.has(mapId)) return;
        
        const latFieldName = $container.data('lat-field-name');
        const lngFieldName = $container.data('lng-field-name');
        const addressFieldName = $container.data('address-field-name');
        const googleApiKey = $container.data('google-api-key');
        const jsCallback = $container.data('js-callback') || ('initGoogleMap_' + mapId);
        const initialLat = parseFloat($container.data('initial-lat')) || 51.505;
        const initialLng = parseFloat($container.data('initial-lng')) || -0.09;
        const defaultZoom = parseInt($container.data('default-zoom')) || 13;
        
        const $latInput = $('[name="' + latFieldName + '"], #' + latFieldName).first();
        const $lngInput = $('[name="' + lngFieldName + '"], #' + lngFieldName).first();
        const $addressInput = $('[name="' + addressFieldName + '"], #' + addressFieldName).first();
        
        const finalLat = ($latInput.val() && !isNaN(parseFloat($latInput.val()))) ? parseFloat($latInput.val()) : initialLat;
        const finalLng = ($lngInput.val() && !isNaN(parseFloat($lngInput.val()))) ? parseFloat($lngInput.val()) : initialLng;
        
        if ($latInput.attr('id')) fieldToMapMap.set($latInput.attr('id'), mapId);
        if ($lngInput.attr('id')) fieldToMapMap.set($lngInput.attr('id'), mapId);
        if ($addressInput.attr('id')) fieldToMapMap.set($addressInput.attr('id'), mapId);
        
        const instance = { mapId, $container, $latInput, $lngInput, $addressInput, finalLat, finalLng, defaultZoom, googleApiKey, type: 'google' };
        mapInstances.set(mapId, instance);
        
        window[jsCallback] = function() {
            createGoogleMap(instance);
        };
        
        if (!$('#google-maps-api-script').length) {
            const script = document.createElement('script');
            script.id = 'google-maps-api-script';
            script.src = 'https://maps.googleapis.com/maps/api/js?key=' + googleApiKey + '&callback=' + jsCallback;
            script.async = true;
            script.defer = true;
            document.head.appendChild(script);
        } else if (typeof google !== 'undefined' && google.maps) {
            createGoogleMap(instance);
        } else {
            setTimeout(function() {
                if (typeof google !== 'undefined' && google.maps) {
                    createGoogleMap(instance);
                } else {
                    $container.html('<div style="padding: 20px; text-align: center; color: red;">Google Maps failed to load</div>');
                }
            }, 2000);
        }
    }

    function createGoogleMap(instance) {
        const mapId = instance.mapId;
        const $container = instance.$container;
        const $latInput = instance.$latInput;
        const $lngInput = instance.$lngInput;
        const $addressInput = instance.$addressInput;
        const finalLat = instance.finalLat;
        const finalLng = instance.finalLng;
        const defaultZoom = instance.defaultZoom;
        
        if (typeof google === 'undefined' || !google.maps) {
            return;
        }
        
        try {
            $container.empty();
            
            const map = new google.maps.Map($container[0], {
                center: { lat: finalLat, lng: finalLng },
                zoom: defaultZoom,
                mapTypeId: 'roadmap'
            });
            
            const marker = new google.maps.Marker({
                position: { lat: finalLat, lng: finalLng },
                map: map,
                draggable: true
            });
            
            instance.map = map;
            instance.marker = marker;
            
            updateCoordinatesDisplay($container, finalLat, finalLng);
            
            marker.addListener('dragend', function() {
                if (isUpdating) return;
                isUpdating = true;
                const lat = marker.getPosition().lat();
                const lng = marker.getPosition().lng();
                updateFieldValue($latInput, lat.toFixed(6));
                updateFieldValue($lngInput, lng.toFixed(6));
                updateCoordinatesDisplay($container, lat, lng);
                
                const geocoder = new google.maps.Geocoder();
                geocoder.geocode({ location: { lat: lat, lng: lng } }, function(results, status) {
                    if (status === 'OK' && results && results[0]) {
                        updateFieldValue($addressInput, results[0].formatted_address);
                    } else if (status === 'ZERO_RESULTS') {
                        updateFieldValue($addressInput, lat.toFixed(6) + ', ' + lng.toFixed(6));
                    } else if (status === 'REQUEST_DENIED') {
                        if (!$container.data('geocode-warning-shown')) {
                            $container.data('geocode-warning-shown', true);
                        }
                        updateFieldValue($addressInput, lat.toFixed(6) + ', ' + lng.toFixed(6));
                    } else {
                        updateFieldValue($addressInput, lat.toFixed(6) + ', ' + lng.toFixed(6));
                    }
                    setTimeout(() => { isUpdating = false; }, 100);
                });
            });
            
            map.addListener('click', function(e) {
                if (isUpdating) return;
                isUpdating = true;
                const lat = e.latLng.lat();
                const lng = e.latLng.lng();
                marker.setPosition(e.latLng);
                updateFieldValue($latInput, lat.toFixed(6));
                updateFieldValue($lngInput, lng.toFixed(6));
                updateCoordinatesDisplay($container, lat, lng);
                
                const geocoder = new google.maps.Geocoder();
                geocoder.geocode({ location: { lat: lat, lng: lng } }, function(results, status) {
                    if (status === 'OK' && results && results[0]) {
                        updateFieldValue($addressInput, results[0].formatted_address);
                    } else if (status === 'REQUEST_DENIED') {
                        updateFieldValue($addressInput, lat.toFixed(6) + ', ' + lng.toFixed(6));
                    } else {
                        updateFieldValue($addressInput, lat.toFixed(6) + ', ' + lng.toFixed(6));
                    }
                    setTimeout(() => { isUpdating = false; }, 100);
                });
            });
            
        } catch (error) {
            $container.html('<div style="padding: 20px; text-align: center; color: red;">Error: ' + error.message + '</div>');
        }
    }

    function updateFieldValue($field, value) {
        if (!$field.length) return;
        if ($field.val() === value) return;
        $field.val(value);
        $field.trigger('change');
        $field.trigger('input');
    }

    function setupGeocodeButton(instance) {
        const $button = instance.$container.closest('.hvnly-map-field-wrapper').find('.hvnly-geocode-address-btn');
        if (!$button.length) return;
        
        $button.off('click').on('click', function(e) {
            e.preventDefault();
            const address = instance.$addressInput.val().trim();
            if (!address) {
                alert('Please enter an address first.');
                return;
            }
            
            $button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Searching...');
            
            if (instance.type === 'google') {
                googleGeocode(address, instance, $button);
            } else {
                leafletGeocode(address, instance, $button);
            }
        });
    }

    function leafletGeocode(address, instance, $button) {
        const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address)}&limit=1`;
        
        $.ajax({
            url: url,
            success: function(data) {
                if (data && data.length > 0) {
                    const lat = parseFloat(data[0].lat);
                    const lng = parseFloat(data[0].lon);
                    const displayName = data[0].display_name;
                    
                    skipReverseGeocode = true;
                    updateFieldValue(instance.$addressInput, displayName);
                    updateFieldValue(instance.$latInput, lat.toFixed(6));
                    updateFieldValue(instance.$lngInput, lng.toFixed(6));
                    
                    instance.map.setView([lat, lng], 15);
                    instance.marker.setLatLng([lat, lng]);
                    updateCoordinatesDisplay(instance.$container, lat, lng);
                    
                    setTimeout(() => { skipReverseGeocode = false; }, 500);
                    alert('Location found!');
                } else {
                    alert('Address not found.');
                }
            },
            error: function() {
                alert('Error searching for address.');
            },
            complete: function() {
                $button.prop('disabled', false).html('<i class="fas fa-map-pin"></i> Get Coordinates');
            }
        });
    }

    function googleGeocode(address, instance, $button) {
        const geocoder = new google.maps.Geocoder();
        geocoder.geocode({ address: address }, function(results, status) {
            if (status === 'OK' && results && results[0]) {
                const location = results[0].geometry.location;
                const lat = location.lat();
                const lng = location.lng();
                
                skipReverseGeocode = true;
                updateFieldValue(instance.$addressInput, results[0].formatted_address);
                updateFieldValue(instance.$latInput, lat.toFixed(6));
                updateFieldValue(instance.$lngInput, lng.toFixed(6));
                
                instance.map.setCenter({ lat: lat, lng: lng });
                instance.map.setZoom(15);
                instance.marker.setPosition({ lat: lat, lng: lng });
                updateCoordinatesDisplay(instance.$container, lat, lng);
                
                setTimeout(() => { skipReverseGeocode = false; }, 500);
                alert('Location found!');
            } else if (status === 'REQUEST_DENIED') {
                alert('Geocoding API is not enabled. Please check your Google Cloud Console.');
            } else {
                alert('Address not found: ' + status);
            }
            $button.prop('disabled', false).html('<i class="fas fa-map-pin"></i> Get Coordinates');
        });
    }

    function reverseGeocodeAddress(lat, lng, $addressInput, $container) {
        if (skipReverseGeocode) {
            return;
        }
        
        if (reverseGeocodeTimeout) {
            clearTimeout(reverseGeocodeTimeout);
        }
        
        reverseGeocodeTimeout = setTimeout(function() {
            const url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18`;
            
            $.ajax({
                url: url,
                timeout: 5000,
                success: function(data) {
                    if (data && data.display_name) {
                        const currentAddress = $addressInput.val();
                        if (currentAddress !== data.display_name && !skipReverseGeocode) {
                            updateFieldValue($addressInput, data.display_name);
                        }
                    }
                }
            });
        }, 300);
    }

    function setupFieldSync() {
        $(document).on('change input', 'input[id$="_latitude"]', function() {
            if (isUpdating) return;
            const $input = $(this);
            const lat = parseFloat($input.val());
            const mapInstance = findMapInstance($input);
            if (!mapInstance || isNaN(lat)) return;
            
            isUpdating = true;
            let lng = parseFloat(mapInstance.$lngInput.val());
            if (isNaN(lng)) lng = 0;
            
            if (mapInstance.type === 'leaflet' && mapInstance.map) {
                mapInstance.map.setView([lat, lng], 15);
                mapInstance.marker.setLatLng([lat, lng]);
            } else if (mapInstance.type === 'google' && mapInstance.map) {
                mapInstance.map.setCenter({ lat: lat, lng: lng });
                mapInstance.marker.setPosition({ lat: lat, lng: lng });
                google.maps.event.trigger(mapInstance.marker, 'dragend');
            }
            
            updateCoordinatesDisplay(mapInstance.$container, lat, lng);
            if (mapInstance.type !== 'google') {
                if (mapInstance.$addressInput.length && lat !== 0 && lng !== 0) {
                    reverseGeocodeAddress(lat, lng, mapInstance.$addressInput, mapInstance.$container);
                }
            }
            setTimeout(() => { isUpdating = false; }, 100);
        });
        
        $(document).on('change input', 'input[id$="_longitude"]', function() {
            if (isUpdating) return;
            const $input = $(this);
            const lng = parseFloat($input.val());
            const mapInstance = findMapInstance($input);
            if (!mapInstance || isNaN(lng)) return;
            
            isUpdating = true;
            let lat = parseFloat(mapInstance.$latInput.val());
            if (isNaN(lat)) lat = 0;
            
            if (mapInstance.type === 'leaflet' && mapInstance.map) {
                mapInstance.map.setView([lat, lng], 15);
                mapInstance.marker.setLatLng([lat, lng]);
            } else if (mapInstance.type === 'google' && mapInstance.map) {
                mapInstance.map.setCenter({ lat: lat, lng: lng });
                mapInstance.marker.setPosition({ lat: lat, lng: lng });
                google.maps.event.trigger(mapInstance.marker, 'dragend');
            }
            
            updateCoordinatesDisplay(mapInstance.$container, lat, lng);
            if (mapInstance.type !== 'google') {
                if (mapInstance.$addressInput.length && lat !== 0 && lng !== 0) {
                    reverseGeocodeAddress(lat, lng, mapInstance.$addressInput, mapInstance.$container);
                }
            }
            setTimeout(() => { isUpdating = false; }, 100);
        });
    }

    function findMapInstance($input) {
        const inputId = $input.attr('id');
        if (inputId && fieldToMapMap.has(inputId)) {
            return mapInstances.get(fieldToMapMap.get(inputId));
        }
        return null;
    }

    function setupAddressAutocomplete() {
        $('input[id$="_address"][type="text"]').each(function() {
            const $input = $(this);
            if ($input.data('autocomplete-initialized')) return;
            
            if (!$input.siblings('.hvnly-address-suggestions').length) {
                const $suggestions = $('<div class="hvnly-address-suggestions"></div>').css({
                    display: 'none', position: 'absolute', top: '100%', left: 0, right: 0,
                    background: '#fff', border: '1px solid #ccc', borderTop: 'none',
                    zIndex: 10000, maxHeight: '200px', overflowY: 'auto', boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
                });
                $input.parent().css('position', 'relative').append($suggestions);
            }
            
            setupAutocompleteForInput($input);
            $input.data('autocomplete-initialized', true);
        });
    }

    function setupAutocompleteForInput($input) {
        const $suggestions = $input.siblings('.hvnly-address-suggestions');
        let debounceTimer;
        
        $input.off('input');
        $input.on('input', function() {
            clearTimeout(debounceTimer);
            const query = $(this).val().trim();
            if (query.length < 3) {
                $suggestions.hide().empty();
                return;
            }
            debounceTimer = setTimeout(() => {
                fetchSuggestions(query, $suggestions, $input);
            }, 500);
        });
        
        $(document).off('click', '.hvnly-address-suggestions div');
        $(document).on('click', '.hvnly-address-suggestions div', function(e) {
            e.preventDefault();
            e.stopPropagation();
            
            const $item = $(this);
            const lat = $item.data('lat');
            const lng = $item.data('lng');
            const displayName = $item.data('displayname');
            const $addressField = $item.closest('.hvnly__dyamic_metabox_tab__field-input').find('input[id$="_address"]');
            
            if (!$addressField.length) {
                return;
            }
            
            skipReverseGeocode = true;
            updateFieldValue($addressField, displayName);
            
            const addressId = $addressField.attr('id');
            const mapId = fieldToMapMap.get(addressId);
            const instance = mapInstances.get(mapId);
            
            if (instance) {
                if (instance.$latInput && instance.$latInput.length) {
                    updateFieldValue(instance.$latInput, lat.toFixed(6));
                }
                if (instance.$lngInput && instance.$lngInput.length) {
                    updateFieldValue(instance.$lngInput, lng.toFixed(6));
                }
                
                if (instance.type === 'leaflet' && instance.map) {
                    instance.map.setView([lat, lng], 15);
                    instance.marker.setLatLng([lat, lng]);
                } else if (instance.type === 'google' && instance.map) {
                    instance.map.setCenter({ lat: lat, lng: lng });
                    instance.marker.setPosition({ lat: lat, lng: lng });
                }
                
                updateCoordinatesDisplay(instance.$container, lat, lng);
            }
            
            setTimeout(() => { skipReverseGeocode = false; }, 1000);
            $suggestions.hide().empty();
        });
        
        $(document).on('click', function(e) {
            if (!$(e.target).closest('.hvnly-address-suggestions').length && !$(e.target).is($input)) {
                $suggestions.hide().empty();
            }
        });
    }

    function fetchSuggestions(query, $container, $addressInput) {
        const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&limit=5`;
        
        $.ajax({
            url: url,
            success: function(data) {
                $container.empty();
                if (!data || data.length === 0) {
                    $container.append('<div style="padding: 10px; color: #666;">No addresses found</div>');
                } else {
                    data.forEach(function(suggestion) {
                        const lat = parseFloat(suggestion.lat);
                        const lng = parseFloat(suggestion.lon);
                        const displayName = suggestion.display_name;
                        
                        $('<div></div>')
                            .css({ padding: '8px 12px', cursor: 'pointer', borderBottom: '1px solid #eee', fontSize: '13px' })
                            .text(displayName)
                            .data({ lat: lat, lng: lng, displayname: displayName })
                            .on('mouseenter', function() { $(this).css('backgroundColor', '#f5f5f5'); })
                            .on('mouseleave', function() { $(this).css('backgroundColor', ''); })
                            .appendTo($container);
                    });
                }
                $container.show();
            },
            error: function() {
                $container.html('<div style="padding: 10px; color: #d63638;">Error loading suggestions</div>').show();
            }
        });
    }

    function updateCoordinatesDisplay($container, lat, lng) {
        const $info = $container.closest('.hvnly-meta-input').siblings('.hvnly-meta-label').find('.coordinates-info');
        if ($info.length) {
            $info.find('.current-lat').text(lat.toFixed(6));
            $info.find('.current-lng').text(lng.toFixed(6));
        }
    }

})(jQuery);