/**
 * FormForge Map Address Field
 *
 * Supports Google Maps and Yandex Maps.
 * Loaded conditionally only on pages with map_address fields.
 */
(function () {
    'use strict';

	var provider = window.formforgeMapProvider || 'google';
	var hasYandexSuggest = !!window.formforgeMapHasYandexSuggest;
	var yandexGeocoder = window.formforgeMapYandexGeocoder || {};
	var hasYandexGeocoderProxy = !!(yandexGeocoder.enabled && yandexGeocoder.ajaxUrl && yandexGeocoder.nonce);
	var messages = window.formforgeMapMessages || {};
    function msg(key, fallback) {
        return messages[key] || fallback;
    }

    document.addEventListener('DOMContentLoaded', function () {
        var containers = document.querySelectorAll('.formforge-map-address');
        if (!containers.length) return;

        containers.forEach(function (container) {
            var lat = parseFloat(container.dataset.lat) || 41.311158;
            var lng = parseFloat(container.dataset.lng) || 69.279737;
            var zoom = parseInt(container.dataset.zoom, 10) || 13;
            var fieldName = container.dataset.fieldName;

            var input = container.querySelector('.formforge-map-input');
            var mapDiv = container.querySelector('.formforge-map-container');
            var latInput = document.getElementById(fieldName + '_lat');
            var lngInput = document.getElementById(fieldName + '_lng');
            var fmtInput = document.getElementById(fieldName + '_formatted');
            if (!input || !mapDiv || !latInput || !lngInput || !fmtInput) return;

            if (provider === 'yandex') {
                initYandex(container, mapDiv, input, latInput, lngInput, fmtInput, lat, lng, zoom);
            } else {
                input.addEventListener('input', function () {
                    latInput.value = '';
                    lngInput.value = '';
                    fmtInput.value = '';
                    setFieldError(container, input, msg('manualInput', 'Select an address suggestion or choose a point on the map.'));
                });
                initGoogle(container, mapDiv, input, latInput, lngInput, fmtInput, lat, lng, zoom);
            }
        });
    });

    function initGoogle(container, mapDiv, input, latInput, lngInput, fmtInput, lat, lng, zoom) {
        if (typeof google === 'undefined' || !google.maps) return;

        var center = { lat: lat, lng: lng };
        var map = new google.maps.Map(mapDiv, { center: center, zoom: zoom });
        var marker = new google.maps.Marker({ position: center, map: map, draggable: true });
        var geocoder = new google.maps.Geocoder();

        // Autocomplete
        var autocomplete = new google.maps.places.Autocomplete(input, { types: ['address'] });
        autocomplete.bindTo('bounds', map);

        autocomplete.addListener('place_changed', function () {
            var place = autocomplete.getPlace();
            if (!place.geometry) {
                setFieldError(container, input, msg('chooseFullAddress', 'Choose a full address suggestion or pick a point on the map.'));
                return;
            }

            map.setCenter(place.geometry.location);
            map.setZoom(17);
            marker.setPosition(place.geometry.location);

            updateValues(
                place.geometry.location.lat(),
                place.geometry.location.lng(),
                place.formatted_address || input.value
            );
        });

        // Click on map
        map.addListener('click', function (e) {
            marker.setPosition(e.latLng);
            reverseGeocodeGoogle(geocoder, e.latLng);
        });

        // Drag marker
        marker.addListener('dragend', function () {
            var pos = marker.getPosition();
            reverseGeocodeGoogle(geocoder, pos);
        });

        function reverseGeocodeGoogle(geocoder, latlng) {
            geocoder.geocode({ location: latlng }, function (results, status) {
                if (status === 'OK' && results[0]) {
                    input.value = results[0].formatted_address;
                    updateValues(latlng.lat(), latlng.lng(), results[0].formatted_address);
                } else {
                    var fallback = coordsText(latlng.lat(), latlng.lng());
                    input.value = fallback;
                    updateValues(latlng.lat(), latlng.lng(), fallback);
                }
            });
        }

        function updateValues(lat, lng, formatted) {
            latInput.value = lat;
            lngInput.value = lng;
            fmtInput.value = formatted || input.value || coordsText(lat, lng);
            setFieldError(container, input, '');
        }
    }

    function initYandex(container, mapDiv, input, latInput, lngInput, fmtInput, lat, lng, zoom) {
        if (typeof ymaps === 'undefined') {
            setFieldError(container, input, msg('yandexUnavailable', 'Yandex Maps is unavailable. Check the API key and try again.'));
            return;
        }

        ymaps.ready(function () {
            var map = new ymaps.Map(mapDiv, { center: [lat, lng], zoom: zoom, controls: ['zoomControl'] });
            var placemark = new ymaps.Placemark([lat, lng], {}, { draggable: true });
            var pendingResolve = null;
            var pendingResolveAddress = '';
            var pendingResolveVersion = 0;
            var inputVersion = 0;
            var suggestView = null;
            var suggestResetTimer = null;
            map.geoObjects.add(placemark);

            function resolveYandexAddress(address) {
                address = (address || '').trim();
                if (!address) return Promise.resolve(false);
                var requestVersion = inputVersion;
                if (pendingResolve && pendingResolveAddress === address && pendingResolveVersion === requestVersion) {
                    return pendingResolve;
                }
                pendingResolveAddress = address;
                pendingResolveVersion = requestVersion;
                pendingResolve = geocodeYandexAddress(address).then(function (result) {
                    if (requestVersion !== inputVersion || input.value.trim() !== address) {
                        return false;
                    }
                    if (!result) {
                        markUnresolved(address);
                        return false;
                    }
                    applyYandexGeocodeResult(result, address);
                    return true;
                }, function () {
                    if (requestVersion !== inputVersion || input.value.trim() !== address) {
                        return false;
                    }
                    markUnresolved(address);
                    return false;
                }).then(function (resolved) {
                    if (pendingResolveVersion === requestVersion && pendingResolveAddress === address) {
                        clearPendingResolve();
                    }
                    return resolved;
                }, function (err) {
                    if (pendingResolveVersion === requestVersion && pendingResolveAddress === address) {
                        clearPendingResolve();
                    }
                    throw err;
                });
                return pendingResolve;
            }

            container.formforgeResolveMapAddress = function () {
                return resolveYandexAddress(input.value);
            };

            function initYandexSuggestView() {
                if (!hasYandexSuggest || !ymaps.SuggestView || suggestView) {
                    return suggestView;
                }
                var suggestContainer = input.closest('.formforge-map-suggest-shell') || container;
	                try {
	                    suggestView = new ymaps.SuggestView(input, {
                            container: suggestContainer,
                            offset: [0, 6],
                            zIndex: 100000
                    });
                    suggestView.events.add('select', function (e) {
                        cancelYandexSuggestReset();
                        var item = e.get('item') || {};
                        var selected = item.value || item.displayName || input.value;
                        inputVersion++;
                        input.value = selected;
                        latInput.value = '';
                        lngInput.value = '';
                        fmtInput.value = '';
                        setFieldError(container, input, '');
                        closeYandexSuggest(suggestView);
                        resolveYandexAddress(selected);
                    });
	                    suggestView.events.add('error', function () {
	                        setFieldError(container, input, msg('yandexSuggestUnavailable', 'Yandex address suggestions are unavailable. Check the Yandex Suggest API key or press Enter to resolve a typed address.'));
	                    });
	                } catch (e) {
                        suggestView = null;
	                    setFieldError(container, input, msg('yandexSuggestUnavailable', 'Yandex address suggestions are unavailable. Check the Yandex Suggest API key or press Enter to resolve a typed address.'));
	                }
                return suggestView;
            }

            function cancelYandexSuggestReset() {
                if (!suggestResetTimer) return;
                clearTimeout(suggestResetTimer);
                suggestResetTimer = null;
            }

            function destroyYandexSuggestView() {
                cancelYandexSuggestReset();
                var currentSuggest = suggestView;
                closeYandexSuggest(currentSuggest);
                if (currentSuggest && typeof currentSuggest.destroy === 'function') {
                    try {
                        currentSuggest.destroy();
                    } catch (e) {}
                }
                if (suggestView === currentSuggest) {
                    suggestView = null;
                }
            }

            function scheduleYandexSuggestReset() {
                cancelYandexSuggestReset();
                suggestResetTimer = setTimeout(function () {
                    suggestResetTimer = null;
                    destroyYandexSuggestView();
                }, 120);
            }

            function clearPendingResolve() {
                pendingResolve = null;
                pendingResolveAddress = '';
                pendingResolveVersion = 0;
            }

	            if (hasYandexSuggest && ymaps.SuggestView) {
                initYandexSuggestView();
	            }

            input.addEventListener('input', function () {
                inputVersion++;
                latInput.value = '';
                lngInput.value = '';
                fmtInput.value = '';
                if (!input.value.trim()) {
                    clearPendingResolve();
                    setFieldError(container, input, '');
                    destroyYandexSuggestView();
                    return;
                }
                cancelYandexSuggestReset();
                initYandexSuggestView();
                setFieldError(container, input, '');
            });
            input.addEventListener('focus', function () {
                cancelYandexSuggestReset();
                initYandexSuggestView();
                if (!input.value.trim()) {
                    closeYandexSuggest(suggestView);
                }
            });
            input.addEventListener('keydown', function (e) {
                if (e.key !== 'Enter') return;
                e.preventDefault();
                resolveYandexAddress(input.value);
            });
            input.addEventListener('blur', function () {
                scheduleYandexSuggestReset();
            });
            attachYandexSubmitGuard(input.closest('form'));

            // Click on map
            map.events.add('click', function (e) {
                var coords = e.get('coords');
                inputVersion++;
                placemark.geometry.setCoordinates(coords);
                reverseGeocodeYandex(coords, inputVersion);
            });

            // Drag marker
            placemark.events.add('dragend', function () {
                var coords = placemark.geometry.getCoordinates();
                inputVersion++;
                reverseGeocodeYandex(coords, inputVersion);
            });

            function reverseGeocodeYandex(coords, requestVersion) {
                geocodeYandexCoords(coords).then(function (result) {
                    if (requestVersion !== inputVersion) {
                        return;
                    }
                    if (result) {
                        input.value = result.address || coordsText(result.lat, result.lng);
                        updateValues(result.lat, result.lng, input.value);
                        return;
                    }
                    var fallback = coordsText(coords[0], coords[1]);
                    input.value = fallback;
                    updateValues(coords[0], coords[1], fallback);
                }, function () {
                    if (requestVersion !== inputVersion) {
                        return;
                    }
                    var fallback = coordsText(coords[0], coords[1]);
                    input.value = fallback;
                    updateValues(coords[0], coords[1], fallback);
                });
            }

            function geocodeYandexAddress(address) {
                if (hasYandexGeocoderProxy) {
                    return geocodeYandexViaProxy({ query: address });
                }
                return geocodeYandexViaJsApi(address);
            }

            function geocodeYandexCoords(coords) {
                if (hasYandexGeocoderProxy) {
                    return geocodeYandexViaProxy({ lat: coords[0], lng: coords[1] });
                }
                return geocodeYandexViaJsApi(coords);
            }

            function geocodeYandexViaJsApi(query) {
                return ymaps.geocode(query, { results: 1 }).then(function (res) {
                    var firstGeo = res.geoObjects.get(0);
                    if (!firstGeo) return null;
                    var coords = firstGeo.geometry.getCoordinates();
                    return {
                        lat: coords[0],
                        lng: coords[1],
                        address: firstGeo.getAddressLine() || (Array.isArray(query) ? coordsText(coords[0], coords[1]) : query)
                    };
                }, function () {
                    return null;
                });
            }

            function geocodeYandexViaProxy(payload) {
                if (!window.fetch || !window.URLSearchParams) {
                    return Promise.resolve(null);
                }
                var body = new URLSearchParams();
                body.set('action', 'formforge_yandex_geocode');
                body.set('nonce', yandexGeocoder.nonce);
                if (payload.query) body.set('query', payload.query);
                if (payload.lat !== undefined && payload.lng !== undefined) {
                    body.set('lat', payload.lat);
                    body.set('lng', payload.lng);
                }
                return window.fetch(yandexGeocoder.ajaxUrl, {
                    method: 'POST',
                    credentials: 'same-origin',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
                    body: body.toString()
                }).then(function (response) {
                    return response.json();
                }).then(function (json) {
                    if (!json || !json.success || !json.data) return null;
                    return normalizeProxyGeocodeResult(json.data);
                }, function () {
                    return null;
                });
            }

            function normalizeProxyGeocodeResult(data) {
                var lat = parseFloat(data.lat);
                var lng = parseFloat(data.lng);
                if (!isFinite(lat) || !isFinite(lng)) return null;
                return {
                    lat: lat,
                    lng: lng,
                    address: data.address || coordsText(lat, lng)
                };
            }

            function applyYandexGeocodeResult(result, fallbackAddress) {
                var coords = [result.lat, result.lng];
                var resolved = result.address || fallbackAddress || coordsText(result.lat, result.lng);
                map.setCenter(coords, 17);
                placemark.geometry.setCoordinates(coords);
                input.value = resolved;
                updateValues(result.lat, result.lng, resolved);
            }

            function markUnresolved(address) {
                latInput.value = '';
                lngInput.value = '';
                fmtInput.value = '';
                setFieldError(container, input, msg('unresolved', 'This address could not be resolved. Choose a suggestion or click the map.'));
                input.value = address || input.value || '';
            }

            function updateValues(lat, lng, formatted) {
                latInput.value = lat;
                lngInput.value = lng;
                fmtInput.value = formatted || input.value || coordsText(lat, lng);
                setFieldError(container, input, '');
            }
        });
    }

    function closeYandexSuggest(suggestView) {
        if (!suggestView || !suggestView.state || typeof suggestView.state.set !== 'function') return;
        try {
            suggestView.state.set('items', []);
            suggestView.state.set('activeIndex', null);
            suggestView.state.set('panelClosed', true);
        } catch (e) {
            // Yandex owns the panel; if its state manager refuses a field,
            // clearing the validation state is still the important UX fix.
        }
    }

    function attachYandexSubmitGuard(form) {
        if (!form || form.formforgeYandexSubmitGuardAttached) return;
        form.formforgeYandexSubmitGuardAttached = true;
        form.addEventListener('pointerdown', function (e) {
            var submitter = findSubmitter(e.target, form);
            if (!submitter) return;
            var pending = findPendingYandexMaps(form);
            if (!pending.length) return;

            e.preventDefault();
            e.stopPropagation();
            if (typeof e.stopImmediatePropagation === 'function') {
                e.stopImmediatePropagation();
            }
            resolvePendingMapsAndSubmit(form, pending, submitter);
        }, true);
        form.addEventListener('submit', function (e) {
            if (form.dataset.formforgeYandexResolvedSubmit === '1') {
                delete form.dataset.formforgeYandexResolvedSubmit;
                return;
            }

            var pending = findPendingYandexMaps(form);

            if (!pending.length) return;

            e.preventDefault();
            e.stopPropagation();
            if (typeof e.stopImmediatePropagation === 'function') {
                e.stopImmediatePropagation();
            }

            var submitter = e.submitter || document.activeElement;
            resolvePendingMapsAndSubmit(form, pending, submitter);
        }, true);
    }

    function findSubmitter(target, form) {
        if (!target || !target.closest) return null;
        var submitter = target.closest('button[type="submit"], input[type="submit"]');
        return submitter && submitter.form === form ? submitter : null;
    }

    function findPendingYandexMaps(form) {
        return Array.prototype.filter.call(
            form.querySelectorAll('.formforge-map-address'),
            function (container) {
                var input = container.querySelector('.formforge-map-input');
                var fieldName = container.dataset.fieldName;
                var latInput = fieldName ? document.getElementById(fieldName + '_lat') : null;
                var lngInput = fieldName ? document.getElementById(fieldName + '_lng') : null;
                return input && input.value.trim() && latInput && lngInput && (!latInput.value || !lngInput.value)
                    && typeof container.formforgeResolveMapAddress === 'function';
            }
        );
    }

    function resolvePendingMapsAndSubmit(form, pending, submitter) {
        Promise.all(pending.map(function (container) {
            return container.formforgeResolveMapAddress();
        })).then(function (results) {
            var allResolved = results.every(Boolean);
            if (!allResolved) return;
            form.dataset.formforgeYandexResolvedSubmit = '1';
            if (typeof window.jQuery === 'function') {
                window.jQuery(form).trigger('submit');
                return;
            }
            if (typeof form.requestSubmit === 'function') {
                try {
                    if (submitter && submitter.form === form) {
                        form.requestSubmit(submitter);
                    } else {
                        form.requestSubmit();
                    }
                } catch (err) {
                    form.requestSubmit();
                }
                return;
            }
            form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
        });
    }

    function setFieldError(container, input, message) {
        var error = container.querySelector('.formforge-map-error');
        if (error) {
            error.textContent = message || '';
            error.style.display = message ? 'block' : 'none';
        }
        if (input && typeof input.setCustomValidity === 'function') {
            input.setCustomValidity(message || '');
        }
    }

    function coordsText(lat, lng) {
        if (lat === '' || lng === '' || lat == null || lng == null) return '';
        return String(lat) + ', ' + String(lng);
    }
})();
