/**
 * @fileoverview app-poi-co.js
 * Zentraler Controller - Bereinigte Fassung ohne Zirkelbezug.
 */

import { updateSinglePoiInStore, getPoiData } from './poi-store.js';
import { UpdateMarkerIcon, isMarkerHighlighted, handlePoiSelectionMap } from './map-poi-mgr.js';
import { BottomSheetCoordinatorInstance } from './bs-gen-co.js';
import { handleClusterSelectionMap, willClusterSpiderfy, updateClusterIconColor } from './map-cluster-mgr.js';
import { showToast, mylog } from './utilities.js';
import { BackendService } from './backend-db.js';
import { InsertPoiUI } from './insert-poi-ui.js';
import { UpdatePoiDetailsUI } from './update-poi-details-ui.js';
import { Poi } from './poi.js';
import { handlePoilessSelectionBs } from './bs-poiless-co.js';
import { updateBsForPoi } from './bs-poi-co.js';
import { handleUpdateStoneDetails } from './update-poi-details-co.js';
import { PhotoSheetUI } from './photo-sheet-ui.js';
import { demarcateRoute, addDirectionHint } from './navigation-co.js';
import { mapState,getMap, getMarkers } from './map-service.js'
import {
    findMarkerByGid,
    removeAddressMarker,
    handlePoilessSelectionMap,
} from './map-gen-mgr.js';
import { BsUI } from './bs-gen-ui.js';
import { handleClusterSelectionBs, attachSwipeListeners } from './bs-cluster-co.js';
// --- 1. SELEKTIONS-LOGIK ---

export async function handlePoiSelection(gid, allParams) {
    const { insertPoiUI, updatePoiUI, origin } = allParams; // origin hier auspacken
    const marker = findMarkerByGid(gid);

    if (!marker || !getMap()) return;

    const activeBs = BsUI.getInstance();

    if (mapState.activeMarkerOrClusterGID === gid) {
        // Nur bei manuellem Klick togglen
        if (origin === 'click') {
            if (activeBs) {
                mylog("[handlePoiSelection] Re-Klick -> Toggle Expansion.");
                await activeBs.toggleExpansion();
                return;
            }
        } else {
            // Verhindert das "Zentrier-Problem" bei doppelter Suche
            mylog("[handlePoiSelection] Re-Suche -> Ignoriere Aktion (Stabilität).");
        }
        //       return; // In beiden Re-Selektions-Fällen: Rest der Funktion überspringen
    }

    // 1. ZUERST UI & Daten aktualisieren
    // Das Bottom-Sheet nimmt seine neue Höhe ein.
    //handlePoiselectionbs
    if (mapState.isRouteActive) {
        demarcateRoute(getMap());
        mapState.isRouteActive = false;
    }
    mylog(`DEBUG 1: handleBsUpdate wird aufgerufen für GID=${gid}`);
    BottomSheetCoordinatorInstance.handleBsUpdate(marker, insertPoiUI, updatePoiUI);

    // 2. DANACH Karte & State aktualisieren
    // Jetzt rechnet fitMapToTarget() mit der korrekten Resthöhe.
    await handlePoiSelectionMap(gid, origin);

    // 3. Suchfeld aktualisieren
    updateSearchFieldWithAddress(gid);
}

// --- 2. EVENT INTERCEPTOREN ---

export function initPoiEventInterceptors(bus, bs, backend, insertUI, updateUI) {
    const activeBus = bus;
    const activeBs = bs || BsUI.getInstance();
    const activeInsertUI = insertUI || InsertPoiUI.getInstance();
    const activeUpdateUI = updateUI || (UpdatePoiDetailsUI.getInstance ? UpdatePoiDetailsUI.getInstance() : null);
    const activeBackend = backend || (BackendService.getInstance ? BackendService.getInstance() : new BackendService());

    // 1. Coordinator initialisieren
    BottomSheetCoordinatorInstance.init(activeBs, activeBackend, mapState, activeBus, activeInsertUI, activeUpdateUI);

    // --- NEU: DER MISSING LINK (Öffnet das Popup) ---
    activeBus.addEventListener('poi:openInsertPopup', (e) => {
        const { coords, addressData } = e.detail;

        activeInsertUI.showPopup({}, coords);

        // Das "Gehirn" des Speichern-Buttons im Popup
        activeInsertUI.onSaveCallback = (formData) => {
            // Transformation für Poi (Snake Case)
            const stonePayload = {
                first_name: formData.firstName,
                last_name: formData.lastName,
                year_of_birth: formData.birthYear,
                stone_position: formData.position,
                street: addressData.street,
                houseNr: addressData.houseNr,
                localdistrict: addressData.localDistrict || '',
                city: 'Berlin',
                lat: coords.lat,
                lng: coords.lng
            };

            // Signal zum tatsächlichen Speichern geben
            activeBus.dispatchEvent(new CustomEvent('poi:requestInsert', {
                detail: { stoneData: stonePayload, sourceUI: activeBs }
            }));
        };
    });

    // --- 2. BACKEND INSERT (Nur noch EINMAL vorhanden) ---
    activeBus.addEventListener('poi:requestInsert', async (event) => {
        const { stoneData, sourceUI } = event.detail;

        try {
            const PoiInstance = new Poi(stoneData);
            const result = await activeBackend.insertStone(PoiInstance);

            if (result.success) {
                const newGid = Number(result.gid);
                PoiInstance.gid = newGid;
                const timerName = `Refresh-Timer-GID-${newGid}`;

                console.time(timerName);
                mylog(`[Coordinator] Starte Refresh für GID: ${newGid}`);

                // 1. ZUERST den Listener registrieren
                // Wir nutzen activeBus, da dies die Referenz auf ApplicationCoordinator.appEvents ist
                activeBus.addEventListener('map:refreshMarkersDone', (e) => {
                    mylog(`[Coordinator] Rückmeldung von Map erhalten für GID: ${e.detail.gid}`);

                    if (Number(e.detail.gid) === newGid) {
                        console.timeEnd(timerName);

                        // Wir geben Leaflet 300ms Zeit, um den Marker physisch 
                        // zu verarbeiten und in die Cluster-Struktur zu integrieren.
                        setTimeout(() => {
                            const marker = findMarkerByGid(newGid);
                            const markersGroup = getMarkers();
                            let clusterRef = null;

                            if (marker && markersGroup) {
                                const vp = markersGroup.getVisibleParent(marker);
                                if (vp && vp !== marker) {
                                    clusterRef = vp;
                                }
                            }

                            mylog(`[Coordinator] Timeout beendet. Selektiere GID: ${newGid} (Cluster: ${!!clusterRef})`);

                            activeBus.dispatchEvent(new CustomEvent('poi:requestSelection', {
                                detail: {
                                    gid: newGid,
                                    parentCluster: clusterRef
                                }
                            }));
                        }, 300);
                    }
                }, { once: true });
                // 2. JETZT den Rest ausführen
                updateSinglePoiInStore(PoiInstance);
                showToast("Stolperstein erfolgreich gespeichert!", 'success');

                activeInsertUI.closePopup();
                if (sourceUI) sourceUI.close();
                removeAddressMarker();

                // 3. Map triggern
                activeBus.dispatchEvent(new CustomEvent('map:refreshMarkers', {
                    detail: {
                        newGid: newGid,
                        coords: { lat: stoneData.lat, lng: stoneData.lng }
                    }
                }));

            } else {
                showToast(`Fehler: ${result.error || 'Serverfehler'}`, 'error');
            }
        } catch (err) {
            console.error("Insert Error:", err);
            showToast("Fehler beim Speichern.");
        }
    });
    // poi update verarbeitung
    activeBus.addEventListener('poi:requestEdit', (e) => {
        const { gid } = e.detail;
        const poiData = getPoiData(gid);

        if (!activeUpdateUI || !poiData) return;

        // 1. UI öffnen
        activeUpdateUI.showPopup(poiData, async (updatedData) => {

            // 2. Logik ausführen (Backend)
            const result = await handleUpdateStoneDetails(updatedData, activeBackend);

            if (result && result.success) {
                // 3. UI Schließen
                activeUpdateUI.closePopup();

                // 4. Daten-Konsistenz sichern
                updateSinglePoiInStore(updatedData);

                // 5. Marker auf Karte aktualisieren
                const marker = findMarkerByGid(gid);
                if (marker) UpdateMarkerIcon(marker, true);

                // 6. BottomSheet Inhalt auffrischen
                updateBsForPoi(gid, {
                    bottomSheetUI: activeBs,
                    mapState,
                    updatePoiUI: activeUpdateUI,
                    insertPoiUI: activeInsertUI,
                    backendService: activeBackend,
                    addDirectionHint
                });

                //             showToast("Änderungen übernommen", "success");
            }
        });
    });

    // --- 3. SELEKTIONEN & UPDATES ---
    activeBus.addEventListener('cluster:requestSelection', async (e) => {
        const { cluster, origin } = e.detail; // origin hier mit extrahieren

        await handleClusterSelection(cluster, activeBs, mapState, activeInsertUI, activeUpdateUI, origin);
    });

    activeBus.addEventListener('poi:requestSelection', async (e) => {
        const { gid, parentCluster, origin } = e.detail;

        await handlePoiSelection(gid, {
            bottomSheetUI: activeBs,
            mapState,
            map: getMap(),
            backendService: activeBackend,
            insertPoiUI: activeInsertUI,
            updatePoiUI: activeUpdateUI,
            origin
        }, parentCluster || null); // origin als 4. Parameter
    });
    /*
        activeBus.addEventListener('cluster:executeStatusUpdate', async (e) => {
            const { cluster, isDirty } = e.detail;
            mylog(`[App-POI-CO] Cluster Update angefordert: ${isDirty ? 'Schlecht' : 'Gut'}`);
    
            // Ruft die bereits existierende Methode auf
            await executeClusterStatusUpdate(cluster, isDirty);
        });*/
    activeBus.addEventListener('cluster:executeStatusUpdate', async (e) => {
        // Wir entpacken jetzt 'timeCode' statt 'isDirty'
        const { cluster, timeCode } = e.detail;

        // Log für die Konsole zur Kontrolle
        const statusLabels = { 0: 'Glänzend', 1: 'Gelb (1m)', 2: 'Orange (3m)', 4: 'Stumpf (12m)' };
        mylog(`[App-POI-CO] Cluster Update angefordert: ${statusLabels[timeCode] || 'Code ' + timeCode}`);

        // Ruft die Methode mit dem numerischen Code auf
        await executeClusterStatusUpdate(cluster, timeCode);
    });

    // --- Einzel-POI Update - voraussetzung für photosheet ---
    activeBus.addEventListener('poi:executeStatusUpdate', async (e) => {
        const { gid, isDirty } = e.detail;

        try {
            // 1. Die DB-Operation abwarten
            const result = await executePoiStatusUpdate(gid, isDirty);

            // 2. DATEN-KONSISTENZ: Store lokal aktualisieren
            const poiData = getPoiData(gid);
            if (poiData && result.last_cleaned) {

                poiData.set('last_cleaned', result.last_cleaned);
                updateSinglePoiInStore(poiData);
            }

            // 3. VISUALISIERUNG: Marker-Icon auf der Karte aktualisieren
            const marker = findMarkerByGid(gid);
            if (marker) {
                // A) Tauscht die Farbe des bestehenden Icons aus (kein visuelles Springen im Spiderfy)
                UpdateMarkerIcon(marker, isMarkerHighlighted(gid, mapState));

                // B) Zwingt das übergeordnete Cluster, seine Farbanteile sofort neu zu berechnen
                const markersGroup = getMarkers();
                if (markersGroup && typeof markersGroup.refreshClusters === 'function') {
                    markersGroup.refreshClusters(marker);
                }
            }

            // 4. Erfolg vermelden -> PhotoSheet darf jetzt öffnen
            activeBus.dispatchEvent(new CustomEvent('poi:executeStatusUpdateDone', {
                detail: { gid }
            }));

            mylog(`[App-POI-CO] Status-Update & Marker-Refresh erfolgreich für GID: ${gid}`);
        } catch (err) {
            // Fehler vermelden -> PhotoSheet bleibt zu
            activeBus.dispatchEvent(new CustomEvent('poi:executeStatusUpdateFail', {
                detail: { gid, message: err.message }
            }));
            console.error(`[App-POI-CO] Fehler: ${err.message}`);
        }
    });

    activeBus.addEventListener('poiless:requestSelection', (e) => {
        handlePoilessSelection(e.detail.addressData, activeBs, activeBackend, addDirectionHint);
    });

    activeBus.addEventListener('poi:executeCaretakerUpdate', async (e) => {
        await executeCaretakerUpdate(e.detail.gid, e.detail.isChecked);
    });

    mylog("App-POI-CO: Alle Event-Interceptors bereinigt initialisiert.");
}

// --- 3. EXECUTION LOGIK & HELPER ---

export async function handleClusterSelection(clickedCluster, bottomSheetUI, mapState, insertUI, updateUI, origin) {
    const childMarkers = clickedCluster.getAllChildMarkers();
    const activeBs = BsUI.getInstance();

    // --- 1. PRÜFUNG: Re-Selektion ---
    const currentActiveId = mapState.activeMarkerOrCluster?._leaflet_id;
    const newClusterId = clickedCluster?._leaflet_id;

    if (newClusterId && currentActiveId === newClusterId) {
        if (activeBs && activeBs.isOpen()) {
            if (origin === 'click') {
                mylog("Cluster Re-Selektion via Klick -> Toggle Expansion.");
                await activeBs.toggleExpansion();
                return;
            } else {
                mylog("Cluster Re-Selektion via Auto/Suche -> Ignoriere Toggle.");
            }
            //          return; // Beendet die Funktion hier, um unnötige Map-Moves zu vermeiden
        }
    }

    // --- 2. Geografischer Check (Gatekeeper) ---
    if (!willClusterSpiderfy(clickedCluster)) {
        mylog("Cluster zu weit verteilt. Zoom-Modus.");
        getMap().fitBounds(clickedCluster.getBounds(), { padding: [50, 50] });
        activeBs.close();
        mapState.activeMarkerOrCluster = null;
        return;
    }

    // --- 3. Adress-Synchronisierung ---
    const firstGid = childMarkers[0].options.gid;
    if (childMarkers.length > 0) {
        updateSearchFieldWithAddress(firstGid);
    }

    // --- 4. BS-Logik (Inhalt rendern & Öffnen) ---
    // Das Sheet nimmt jetzt seine Höhe ein, BEVOR die Karte rechnet.
    await handleClusterSelectionBs(clickedCluster, activeBs, firstGid, addDirectionHint);

    // --- 5. Karten-Logik (Zentrieren & Spiderfy) ---
    // WICHTIG: origin wird hier mitgegeben für die Docking-Entscheidung!
    await handleClusterSelectionMap(clickedCluster, activeBs, mapState, origin);

    // --- 6. Swipe-Initialisierung ---
    if (childMarkers.length > 1) {
        const firstGid = childMarkers[0].options.gid;
        window.requestAnimationFrame(() => {
            attachSwipeListeners(activeBs, firstGid);
            mylog("[SUCCESS] Swipe-Logik stabil initialisiert.");
        });
    }
}
export async function executePoiStatusUpdate(gid, timeCode = 0) {
    // Falls irgendwo noch ein Boolean (true für dirty) übergeben wird, 
    // mappen wir ihn sicherheitshalber auf den Standard-Dirty-Code (z.B. 4 für 12 Monate)
    const normalizedCode = (timeCode === true) ? 4 : timeCode;

    mylog(`[Controller] Status-Update via BackendService für GID: ${gid} mit Code: ${normalizedCode}`);

    const backend = BackendService.getInstance();

    // Ruft die aktualisierte Methode im BackendService auf
    const result = await backend.updateStoneCleaningStatus(gid, normalizedCode);

    return result;
}

/*
export async function executePoiStatusUpdate(gid, isDirty) {
    mylog(`[Controller] Status-Update via BackendService für GID: ${gid}`);

    const backend = BackendService.getInstance();

    // Hier wird jetzt die korrekte Methode aufgerufen, 
    // die intern entscheidet, ob 'clean' oder 'dirty' PHP genutzt wird.
    const result = await backend.updateStoneCleaningStatus(gid, isDirty);

    // BackendService wirft bei !response.ok bereits einen Error, 
    // daher landet ein Fehler automatisch im catch-Block des Listeners.
    return result;
}
*/
/**
 * Führt das Status-Update für einen POI aus und öffnet 
 * anschließend das Foto-Dokumentations-Modal.
 */


/*
export async function executeClusterStatusUpdate(cluster, needsCleaning) {
    const childMarkers = cluster.getAllChildMarkers();
    const updatePromises = childMarkers.map(async (m) => {
        const gid = m.options.gid;
        const resp = await BackendService.getInstance().updateStoneCleaningStatus(gid, needsCleaning);
        if (resp?.success) {
            const data = getPoiData(gid);
            if (data) { data.last_cleaned = resp.last_cleaned; data.needs_cleaning = needsCleaning; updateSinglePoiInStore(data); }
            UpdateMarkerIcon(m, isMarkerHighlighted(gid, mapState));
        }
    });
    await Promise.all(updatePromises);
    if (getMarkers()?.refreshClusters) getMarkers().refreshClusters();
    showToast(`${childMarkers.length} Steine im Cluster aktualisiert.`);
}*/
export async function executeClusterStatusUpdate(cluster, timeCode = 0) {
    const childMarkers = cluster.getAllChildMarkers();

    // Logik-Umwandlung für den lokalen Store: 
    // Nur bei Code 0 ist der Stein nicht mehr reinigungsbedürftig.
    const isDirty = timeCode !== 0;

    mylog(`[Cluster-Update] Starte Batch-Update für ${childMarkers.length} Steine (Code: ${timeCode})`);

    const updatePromises = childMarkers.map(async (m) => {
        const gid = m.options.gid;

        // Aufruf des neuen Backend-Service mit dem numerischen Code
        const resp = await BackendService.getInstance().updateStoneCleaningStatus(gid, timeCode);

        if (resp?.success) {
            const data = getPoiData(gid);
            if (data) {
                // Lokalen Datenstand synchronisieren
                data.last_cleaned = resp.last_cleaned;
                data.needs_cleaning = isDirty;
                updateSinglePoiInStore(data);
            }

            // Icon auf der Karte aktualisieren (Farbumschlag)
            UpdateMarkerIcon(m, isMarkerHighlighted(gid, mapState));
        }
    });

    // Alle Updates parallel ausführen
    await Promise.all(updatePromises);

    // Cluster-Darstellung auf der Karte refreshen (wichtig für die Farbsummen im Cluster)
    if (getMarkers()?.refreshClusters) {
        getMarkers().refreshClusters();
    }

    const statusText = isDirty ? "auf 'stumpf' gesetzt" : "als 'glänzend' markiert";
    showToast(`${childMarkers.length} Steine im Cluster ${statusText}.`);
}


export async function executeCaretakerUpdate(gid, isChecked) {
    const result = await BackendService.getInstance().updateStoneCaretakerStatus(gid, isChecked);
    if (result?.success) {
        const data = getPoiData(gid);
        if (data) { data.caretaker = isChecked ? 'anonym' : ''; updateSinglePoiInStore(data); }
        const marker = findMarkerByGid(gid);
        if (marker) UpdateMarkerIcon(marker, isMarkerHighlighted(gid, mapState));
        showToast(`Putzpatenschaft ${isChecked ? 'aktiviert' : 'beendet'}.`);
    }
}

export function handlePoilessSelection(addressData, bottomSheetUI, backendService, addDirectionHint) {


    // 3. Mapstate und UI-Logik delegieren
    handlePoilessSelectionBs(addressData, bottomSheetUI, backendService, addDirectionHint);
    // 1. Karte vorbereiten & Zentrieren
    handlePoilessSelectionMap(addressData, origin);
    // 4. Suchfeld in der App-Shell direkt aktualisieren
    // Wir übergeben das addressData Objekt für die String-Zusammensetzung
    updateSearchFieldWithAddress(null, addressData);
}

/**
 * Hilfsfunktion zum Aktualisieren des Suchfeldes mit einer Adresse.
 * es muss ein objekte mit den  einzlenen felder übergeben werden, damit das auch bei poiless adressen funktioniert.
 * 
 */
export function updateSearchFieldWithAddress(gid, address) {

    const placeholder = document.getElementById('search-address-placeholder-text');
    if (placeholder) {
        if (gid) {
            const poiData = getPoiData(gid);
            if (poiData) {
                // Wenn poiData eine Instanz von Poi ist oder dieses Schema nutzt:
                placeholder.textContent = `${poiData.street || ''} ${poiData.houseNr || ''}${poiData.localdistrict ? ', ' + poiData.localdistrict : ''}`.trim();
            }
        } else if (address) {
            placeholder.textContent = `${address.street || ''} ${address.houseNr || ''}, ${address.localDistrict || ''}`.trim();

        } else {
            placeholder.textContent = 'Name, Adresse, ...';
        }
    }
}