// gui-address-co.js
// Aufgabe: übergeordnete Verantwortung für die komplette Abwicklung einer Adresssuche auf dem gui

import { showToast, wait, mylog } from './sc-utilities.js';
import { handleBsUpdate } from './bs-gen-co.js';
import { BackendService } from './backend-poi.js';
import { mapState, demarcateElement } from './map-gen-mgr.js';
import { highlightCluster, unspiderfyCluster } from './map-cluster-mgr.js';
import { UpdateMarkerIcon } from './map-poi-mgr.js';


let currentAddressMarker = null; // Intern für dieses Modul, verwaltet den Adress-Marker

/**
 * Sucht nach einem POI (Point of Interest) Marker oder dem übergeordneten Cluster eines POI-Markers
 * an bestimmten Koordinaten innerhalb einer definierten Toleranz.
 *
 * @param {L.Map} mapInstance 
 * @param {L.MarkerClusterGroup} markerClusterGroup 
 * @param {number} lat 
 * @param {number} lon 
 * @param {string} searchStreetName 
 * @param {string} searchHouseNumber 
 * @returns {L.Marker | L.MarkerCluster | null} Der gefundene Marker oder Cluster, oder null.
 */
function findPoiAtCoordinates(mapInstance, markerClusterGroup, lat, lon, searchStreetName = '', searchHouseNumber = '') {
    const searchLatLng = L.latLng(lat, lon);
    const toleranceMeters = 100;

    let firstMarkerInTolerance = null;
    const allMarkersInTolerance = [];
    const matchingStreetAndHouseNumberMarkers = [];

    const normalizeStreetName = (name) => {
        if (!name) return '';
        return name.toLowerCase().replace(/\s+/g, ' ').replace(/str\./g, 'straße').trim();
    };

    const normalizeHouseNumber = (number) => {
        let normalized = String(number).toLowerCase().trim();
        return normalized;
    };

    const normalizedSearchStreetName = normalizeStreetName(searchStreetName);
    const hasSearchStreetName = normalizedSearchStreetName.length > 0;
    const normalizedSearchHouseNumber = normalizeHouseNumber(searchHouseNumber);
    const hasSearchHouseNumber = normalizedSearchHouseNumber.length > 0;

    mylog(`DEBUG (findPoiAtCoordinates): Suche POI mit Adresse: "${searchStreetName} ${searchHouseNumber}"`);

    markerClusterGroup.eachLayer(function (layer) {
        if (layer instanceof L.Marker) {
            const markerLatLng = layer.getLatLng();
            const distance = markerLatLng.distanceTo(searchLatLng);

            if (distance <= toleranceMeters) {
                allMarkersInTolerance.push(layer);

                const poiStreetNameRaw = layer.options?.data?.street || '';
                const normalizedPoiStreetName = normalizeStreetName(poiStreetNameRaw);
                const poiHouseNumberRaw = layer.options?.data?.house_no || '';
                const normalizedPoiHouseNumber = normalizeHouseNumber(poiHouseNumberRaw);

                let streetNameMatches = false;
                if (hasSearchStreetName) {
                    streetNameMatches = normalizedPoiStreetName.includes(normalizedSearchStreetName) ||
                        normalizedSearchStreetName.includes(normalizedPoiStreetName);
                } else {
                    streetNameMatches = true;
                }

                let houseNumberMatches = false;
                if (hasSearchHouseNumber) {
                    houseNumberMatches = normalizedPoiHouseNumber === normalizedSearchHouseNumber;
                } else {
                    houseNumberMatches = true;
                }

                const fullAddressMatches = streetNameMatches && houseNumberMatches;

                if (fullAddressMatches) {
                    matchingStreetAndHouseNumberMarkers.push(layer);
                    if (!firstMarkerInTolerance) {
                        firstMarkerInTolerance = layer;
                    }
                }
            }
        }
    });

    if ((hasSearchStreetName || hasSearchHouseNumber) && matchingStreetAndHouseNumberMarkers.length === 1) {
        return matchingStreetAndHouseNumberMarkers[0];
    }
    if (!(hasSearchStreetName || hasSearchHouseNumber) && allMarkersInTolerance.length === 1) {
        return allMarkersInTolerance[0];
    }

    let markerForParentCheck = null;
    if (matchingStreetAndHouseNumberMarkers.length > 0) {
        markerForParentCheck = matchingStreetAndHouseNumberMarkers[0];
    } else if (allMarkersInTolerance.length > 0) {
        markerForParentCheck = allMarkersInTolerance[0];
    }

    if (!markerForParentCheck) {
        return null;
    }

    const visibleParent = markerClusterGroup.getVisibleParent(markerForParentCheck);

    if (visibleParent) {
        if (visibleParent instanceof L.MarkerCluster) {
            mylog(`DEBUG (findPoiAtCoordinates - Ergebnis): Cluster gefunden mit ${visibleParent.getChildCount()} Children.`);
            return visibleParent;
        } else if (visibleParent instanceof L.Marker) {
            mylog(`DEBUG (findPoiAtCoordinates - Ergebnis): Einzelnen Marker gefunden.`);
            return visibleParent;
        }
    }

    return firstMarkerInTolerance;
}


/**
 * Entfernt den Adress-Marker von der Karte, falls vorhanden.
 */
export function removeAddressMarker(map) {
    if (currentAddressMarker) {
        map.removeLayer(currentAddressMarker);
        currentAddressMarker = null;
        mylog("DEBUG (gui-addr-co.js): Adress-Marker entfernt.");
    }
}

/**
 * Behandelt die Logik, wenn eine Adresse auf der Karte gefunden wurde.
 * Navigiert die Karte, setzt Adress-Marker und leitet die POI-Auswertung ein.
 * DIES IST DER ORT DER EHEMALIGEN onAddressSelectedForMap()-LOGIK.
 *
 * @param {L.Map} map - Die Leaflet-Karteninstanz.
 * @param {L.MarkerClusterGroup} markerClusterGroup - Die MarkerClusterGroup-Instanz.
 * @param {object} bottomSheetUI - Das Objekt zur Steuerung des Bottom Sheet UI.
 * @param {object} addressData - Das Adress-Objekt (lat, lon, street, houseNr, district).
 * @param {boolean} isValidPointAddress - Gibt an, ob die Adresse genau genug ist.
 * @param {BackendService} backendService - Eine Instanz des BackendService.
 * @param {function} getAdminModeStatus - Eine Funktion für den Admin-Modus-Status.
 * @param {object} userLocationInstance - Die Instanz des UserLocation-Objekts.
 */
export async function handleFoundAddressOnGui(
    map,
    markerClusterGroup,
    bottomSheetUI,
    addressData,
    isValidPointAddress,
    backendService,
    getAdminModeStatus,
    userLocationInstance
) {
    let lat = addressData.lat;
    let lon = addressData.lon;
    const street = addressData.street;
    const houseNo = addressData.houseNr;
    const district = addressData.district;

    // Vorhandenen Adress-Marker entfernen
    removeAddressMarker(map);

    if (isValidPointAddress) {
        // Logik für exakte Adresse (Hausnummer gefunden)
        const fixedZoomLevel = 19;
        mylog(`DEBUG (gui-addr-co.js): Navigiere Karte zu gesuchter Adresse: (${lat}, ${lon}) auf Zoomstufe ${fixedZoomLevel}`);
        map.setView(L.latLng(lat, lon), fixedZoomLevel);
        await wait(200);

        mylog("DEBUG (gui-addr-co.js): setView-Animation abgeschlossen. Starte POI-Auswertung.");

        const foundPoiOrCluster = findPoiAtCoordinates(map, markerClusterGroup, lat, lon, street, houseNo);

        if (foundPoiOrCluster) {
            // 1. AUFRÄUMEN (Alten Status demarkieren und Unspiderfy)
            if (mapState.activeMarkerOrCluster) {
                demarcateElement(mapState.activeMarkerOrCluster, mapState);
            }
            unspiderfyCluster(markerClusterGroup, mapState);

            // 2. NEUEN STATUS setzen
            mapState.activeMarkerOrCluster = foundPoiOrCluster;

            // 3. HIGHLIGHTING und KARTEN-UPDATE anwenden
            if (foundPoiOrCluster instanceof L.MarkerCluster) {
                 const elementIcon = foundPoiOrCluster._icon;

                 if (elementIcon) {
                     highlightCluster(elementIcon, foundPoiOrCluster, mapState);
                     mylog("DEBUG (gui-addr-co.js): Gefundenes CLUSTER hervorgehoben.");
                 }

                 // Cluster sofort spiderfyen, um die POIs zu zeigen
                 setTimeout(() => {
                    foundPoiOrCluster.spiderfy();
                    mapState.currentlySpiderfiedCluster = foundPoiOrCluster;
                    mylog(`DEBUG Spiderfy: Gefundenes Cluster gespidert.`);
                 }, 200);

            } else if (foundPoiOrCluster instanceof L.Marker) {
                 mylog("DEBUG (gui-addr-co.js): Einzelner Marker gefunden. Wende Marker-Highlight an.");
                 mapState.activeMarkerOrClusterGID = foundPoiOrCluster.options.data.gid;
                 UpdateMarkerIcon(foundPoiOrCluster, true);
            }

            // Bottom Sheet für gefundenen POI/Cluster aktualisieren
            handleBsUpdate(
                foundPoiOrCluster,
                bottomSheetUI,
                mapState,
                userLocationInstance,
                getAdminModeStatus,
                markerClusterGroup,
                map,
                backendService
            );
        } else {
            mylog("WARNUNG (gui-addr-co.js): findPoiAtCoordinates hat nichts Passendes gefunden. Öffne Bottom Sheet im Neuanlage-Modus.");

            // Fallback: Keine POIs gefunden, öffne Bottom Sheet für NEUE Adresse
            
            // Simuliere ein Marker-Objekt für handleBsUpdate im isNewAddressMode
            const tempAddressMarker = L.marker([lat, lon]); 
            tempAddressMarker.options.isNewAddressMode = true;
            tempAddressMarker.options.addressData = addressData;
            
            // Bottom Sheet für Neuanlage öffnen (nimmt das Marker-Objekt als 'element')
            handleBsUpdate(
                tempAddressMarker, // Wird in bs-gen-co als Marker behandelt
                bottomSheetUI,
                mapState,
                userLocationInstance,
                getAdminModeStatus,
                markerClusterGroup,
                map,
                backendService
            );
        }
    } else {
        // Logik für ungenaue Adresse (nur Straße oder Ort gefunden)
        const fixedZoomLevel = 15;
        mylog(`DEBUG (gui-addr-co.js): Ungenaue Adresse, navigiere zu: (${lat}, ${lon}) auf Zoomstufe ${fixedZoomLevel}`);
        map.setView(L.latLng(lat, lon), fixedZoomLevel);
        await wait(200);
        
        // Bottom sheet schließen oder auf eine generische Adressanzeige aktualisieren
        bottomSheetUI.close(); 
        showToast("Die Adresse ist nicht exakt genug. Es wurde kein Marker hervorgehoben.", 'warning');
    }
}

/**
 * Initialisiert den GUI Address Coordinator.
 * Er abonniert das Adressauswahl-Event des ScAddressSheetCoordinator und 
 * implementiert die komplexe Adressvalidierung und -verarbeitung.
 *
 * @param {object} addressCoordinator - Instanz des ScAddressSheetCoordinator (der den Event-Kanal bereitstellt).
 * @param {object} dependencies - Objekt mit allen benötigten Abhängigkeiten (Map, ClusterGroup, Services, etc.).
 */
export function setupGuiAddressCoordinator(addressCoordinator, dependencies) {
    
    // Die Logik, die ausgeführt wird, wenn der AddressCoordinator meldet: "Adresse ausgewählt!"
    addressCoordinator.onAddressSelected(async (addressResult) => {
        
        mylog("DEBUG (gui-addr-co.js): Adresse vom Coordinator empfangen. Starte Validierung..." + addressResult.displayName);

        // Alias für Abhängigkeiten aus dem dependencies-Objekt
        const map = dependencies.map;
        const markers = dependencies.markerClusterGroup; // umbenannt von markers zu markerClusterGroup
        const bottomSheetUI = dependencies.bottomSheetUI;
        const backendService = dependencies.backendService;
        const getAdminModeStatus = dependencies.getAdminModeStatus;
        const userLocation = dependencies.userLocationInstance;

        let finalLat = addressResult.lat;
        let finalLon = addressResult.lon;
        let firstResult = null;


        // Erzeuge ein Standard Objekt, um handleFoundAddressOnGui() zu füttern
        firstResult = {
            lat: addressResult.lat,
            lon: addressResult.lon,
            resultType: 'pointAddress', // Gehen wir davon aus, da präzise
            // Stellen Sie sicher, dass alle benötigten Adress-Komponenten vorhanden sind
            street: addressResult.street || '',
            houseNr: addressResult.houseNr || '',
            city: addressResult.city || 'Berlin',
            district: addressResult.district || '',
            postalCode: addressResult.postcode || '',
            // Weitere Metadaten aus dem POI-Resultat (z.B. für Datenbank-Details)
            customSource: addressResult.source,
            originalPoiId: addressResult.poi_id,
            originalNames: addressResult.names,
            originalDisplay: addressResult.display_name
        };

        const isPreValidated = addressResult.source === 'HERE' || addressResult.source === 'POI';

        if (isPreValidated) {
            // WENN bereits HERE oder POI_DB (bereits präzise geocodet), 
            // überspringe Validierung und nutze direkt die übergebenen Koordinaten/Daten.
            mylog("DEBUG (gui-addr-co.js): Adresse ist vor-validiert (Quelle: " + addressResult.source + "). Überspringe Validierung.");

            await handleFoundAddressOnGui(
                map,
                markers,
                bottomSheetUI,
                firstResult,
                true, // Ist immer als gültiger Punkt zu behandeln
                backendService,
                getAdminModeStatus,
                userLocation,
            );
            return; // Beende die Funktion hier, um die Validierung zu überspringen.
        }

        const NoHouseNr = (addressResult.source === 'OSM' && !addressResult.houseNr);
        
        if (NoHouseNr) {
            // WENN Hausnr fehlt, überspringe Validierung und nutze direkt die übergebenen Koordinaten/Daten.
            mylog("DEBUG (gui-addr-co.js): Adresse ohne Hnr. Überspringe Validierung.");

            await handleFoundAddressOnGui(
                map,
                markers,
                bottomSheetUI,
                firstResult,
                false, // keine Hnr
                backendService,
                getAdminModeStatus,
                userLocation,
            );
            return; // Beende die Funktion hier, um die Validierung zu überspringen.
        }


        // --- FALL: KEINE VOR-VALIDIERTE QUELLE (MUSS VALIDIEREN) ---
        const validationUrl = `./validate_address.php?q=${encodeURIComponent(addressResult.displayName + ', Berlin')}`;
        let isValidPointAddress = false;

        try {
            const response = await fetch(validationUrl);
            if (!response.ok) {
                const errorText = await response.text();
                throw new Error(`HTTP-Fehler bei Validierung! Status: ${response.status}, Antwort: ${errorText}`);
            }
            const validationData = await response.json();
            mylog("DEBUG (gui-addr-co.js): Validierungsdaten erhalten:", validationData);

            if (validationData && validationData.length > 0) {
                firstResult = validationData[0];
                finalLat = firstResult.lat;
                finalLon = firstResult.lon;

                // Prüfen, ob Ergebnis präzise genug ist
                if (firstResult.precision === 'precise' || firstResult.precision === 'interpolated') {
                    isValidPointAddress = true;
                    mylog("DEBUG (gui-addr-co.js): Adresse als 'Point Address' validiert.");
                    
                    // Füttere handleFoundAddressOnGui mit dem VOM VALIDATOR BEREINIGTEN Ergebnis
                    await handleFoundAddressOnGui(
                        map,
                        markers,
                        bottomSheetUI,
                        firstResult,
                        isValidPointAddress,
                        backendService,
                        getAdminModeStatus,
                        userLocation,
                    );
                } else if (firstResult.precision === 'city') {
                    showToast('Adresse nicht spezifisch genug (nur Stadt)', 'warning');
                    map.setView([finalLat, finalLon], 13);
                    removeAddressMarker(map);
                    bottomSheetUI.close();
                    return;
                } else if (firstResult.precision === 'street') {
                    showToast('Adresse nicht spezifisch genug (nur Straße)', 'warning');
                    map.setView([finalLat, finalLon], 16);
                    removeAddressMarker(map);
                    bottomSheetUI.close();
                    return;
                } else {
                    mylog("DEBUG (gui-addr-co.js): Adresse gefunden, aber keine 'Point Address' (Typ:", firstResult.resultType, ").");
                    // Fallback: Nur zentrieren, kein Address-Marker
                    map.setView([finalLat, finalLon], 17);
                    removeAddressMarker(map);
                    bottomSheetUI.close();
                    return;
                }
            } else {
                mylog("WARNUNG (gui-addr-co.js): Keine Ergebnisse von der Validierungs-API erhalten.");
                showToast('Adresse nicht gefunden', 'Fehler');
                bottomSheetUI.close();
                removeAddressMarker(map);
                return;
            }
        } catch (error) {
            mylog("FEHLER (gui-addr-co.js): Fehler bei der Adressvalidierung, fahre aber mit Navigation fort:", error);
            showToast('Fehler bei der Adressvalidierung. Die Karte wird trotzdem zentriert.', 'Fehler');
            // Im Fehlerfall trotzdem zentrieren
            map.setView([finalLat, finalLon], 17);
            bottomSheetUI.close();
            removeAddressMarker(map);
        }
    });

    mylog("INFO (GuiAddrCo): GUI Address Coordinator ist initialisiert und abonniert Adress-Events.");
}


//import { addressSheetEventNames } from './gui-address-co.js';

// ... (Andere Imports wie Leaflet oder Custom Controls)

// Konstante für das Karten-Padding, wenn das Bottom Sheet geöffnet ist.
// Dies muss dem Padding in Ihrer CSS/Tailwind-Konfiguration entsprechen.
const PADDING_BOTTOM = [0, 250]; // Beispiel: 250px Bottom Padding für das Sheet.

/**
 * Richtet alle Event-Listener ein, die die Karten-Ansicht mit dem Address Coordinator 
 * und dem Bottom Sheet koordinieren.
 * * @param {L.Map} map Die Leaflet-Instanz.
 * @param {AddressCoordinator} addressCoordinator Die Instanz des Address Coordinators, der Events sendet.
 * @param {UserLocationManager} userLocationMgr Der Manager für die Benutzerposition.
 * @param {BottomSheetUI} bottomSheetUI Die Instanz für das generische Bottom Sheet.
 * @param {AddressCoordinator} addressSheetUI Die Instanz, die den Button-Zustand im Adress-Sheet aktualisiert.
 */
/*
export function setupAddressCoordinatorListeners(
    map,
    addressCoordinator,
    userLocationMgr,
    bottomSheetUI,
    addressSheetUI
) {
    // -----------------------------------------------------------------
    // 1. Event: Benutzer wählt eine neue Position (Adresse oder Klick auf Karte)
    // -----------------------------------------------------------------
    addressCoordinator.on(addressSheetEventNames.CURRENT_LOCATION_SELECTED, (event) => {
        const { lat, lon, isUserLocation } = event.detail;

        // 1.1. Neue Karten-Ansicht zentrieren
        if (lat && lon) {
            const newCenter = L.latLng(lat, lon);
            
            // Setzt das Karten-Padding (wenn das Bottom Sheet geöffnet ist), bevor zentriert wird.
            // Dies ist wichtig, damit die gewählte Markierung nicht unter dem Sheet verschwindet.
            map.setZoom(16);
            map.panTo(newCenter, { paddingBottomRight: bottomSheetUI.isSheetOpen() ? PADDING_BOTTOM : [0, 0] });
        }

        // 1.2. UI-Feedback für den "Aktuelle Position verwenden"-Button im Address Sheet aktualisieren
        // Der Button ist nur aktiv, wenn die gewählte Position NICHT die aktuelle GPS-Position ist.
        addressSheetUI.updateCurrentLocationButton(!isUserLocation);

        // 1.3. Das Address Sheet nach der Auswahl schließen
        addressCoordinator.closeSheet(); 
        
        // 1.4. Prüfen, ob die gewählte Position die aktuelle Benutzerposition ist
        if (isUserLocation) {
             // Wenn die gewählte Position die GPS-Position ist, den Benutzerort Button deaktivieren
             userLocationMgr.disableUserLocationButton();
        } else {
             // Wenn eine andere Adresse gewählt wurde, den Benutzerort Button wieder aktivieren
             userLocationMgr.enableUserLocationButton();
        }
    });

    // -----------------------------------------------------------------
    // 2. Event: Bottom Sheet Zustand ändert sich (z.B. öffnet/schließt sich)
    // -----------------------------------------------------------------
    bottomSheetUI.on('sheetStateChange', (event) => {
        const isOpen = event.detail.isOpen;
        
        // Aktualisiert das Karten-Padding, um das Bottom Sheet zu berücksichtigen.
        // Das Padding sorgt dafür, dass die Karte relativ zum sichtbaren Bereich verschoben wird.
        const padding = isOpen ? PADDING_BOTTOM : [0, 0];
        
        // map.paddingBottomRight ist eine benutzerdefinierte Leaflet-Erweiterung 
        // oder die Option, die beim panTo/fitBounds verwendet wird.
        map.panBy(map.getPixelOrigin().add(L.point(0, 0)), { paddingBottomRight: padding });
    });

    // -----------------------------------------------------------------
    // 3. Event: Address Sheet wird geöffnet
    // -----------------------------------------------------------------
    addressCoordinator.on(addressSheetEventNames.SHEET_OPENED, () => {
        // Prüfen Sie beim Öffnen des Sheets, ob die aktuell auf der Karte 
        // gewählte Position die GPS-Position des Benutzers ist.
        const isUserLocationSelected = userLocationMgr.isUserLocationSelected();

        // Wenn die GPS-Position gewählt ist, sollte der Button "Aktuelle Position verwenden" 
        // deaktiviert sein, da sie bereits verwendet wird.
        addressSheetUI.updateCurrentLocationButton(!isUserLocationSelected);
    });
}
*/
// ... (Andere Funktionen wie initMap, fetchPOIs, setupMapEventListeners, etc.)