// addrs-co.js
// Dieses Modul koordiniert die Interaktion zwischen der Adress-UI (addrs-ui.js) 
// und der Karte (showmap.js), indem es Suchanfragen durchführt und Events weiterleitet.

import { ScAddressSheetUI } from './addrs-ui.js';
import { showToast } from './sc-utilities.js';
import { BackendService } from './backend-poi.js';
import { fetchAdressesFromHere, fetchAdressesFromNominatim } from './backend-geo.js';

export class ScAddressSheetCoordinator {

    constructor(addressSheetUI, backendService) {
        this.addressSheetUI = addressSheetUI;
        this.backendService = backendService;
        this.searchTimeout = null;

        // Callbacks für die Events, die showmap.js abfangen soll
        this.addressSelectedCallbacks = [];
        this.currentLocationSelectedCallbacks = [];
        this.stateChangeCallbacks = [];
        this.sheetTriggeredCallbacks = [];
        this.searchEnteredAddressCallbacks = [];

        this._addEventListeners();
        console.log("ScAddressSheetCoordinator initialisiert (volle Entkopplung).");
    }

    /**
     * Registriert alle notwendigen Event-Listener.
     * @private
     */
    _addEventListeners() {
        this.addressSheetUI.onSearchInput(this.handleSearchInput.bind(this));
           /**
     * Registriert einen Callback für das "addressSelectedForMap"-Event.
     * Dies wird vom aufrufenden Code als addressCoordinator.onAddressSelected erwartet.
     * @param {function({lat: number, lon: number, displayName: string, source: string}): void} callback
     */
        this.addressSheetUI.onAddressSelected(this._handleAddressSelectedFromUI.bind(this));
        /**
     * Registriert einen Callback für das "currentLocationSelected"-Event.
     * @param {function(): void} callback
     */
       this.addressSheetUI.onCurrentLocationSelected(this._handleCurrentLocationSelectedFromUI.bind(this));
        this.addressSheetUI.onStateChange(this._handleStateChangeFromUI.bind(this));
        this.addressSheetUI.onSheetTriggered(this._handleSheetTriggeredFromUI.bind(this));
        this.addressSheetUI.onSearchEnteredAddress(this._handleSearchEnteredAddressFromUI.bind(this));
    }

    /**
     * Handler für das UI-Event "addressSelected".
     * Löst ein Coordinator-Event "addressSelectedForMap" aus.
     * @param {{lat: number, lon: number, displayName: string, source: string}} addressData
     * @private
     */
    _handleAddressSelectedFromUI(addressData) {
        console.log("DEBUG (Coordinator): Adresse vom UI ausgewählt, gebe 'addressSelectedForMap' Event weiter.", addressData);
        this.addressSheetUI.closeResults();
        this._emit('addressSelectedForMap', addressData);
    }

    /**
     * Handler für das UI-Event "currentLocationSelected".
     * Löst ein Coordinator-Event "currentLocationSelected" aus.
     * @private
     */
    _handleCurrentLocationSelectedFromUI() {
        console.log('DEBUG (Coordinator): "Aktueller Standort" vom UI ausgewählt, gebe "currentLocationSelected" Event weiter.');
        this.addressSheetUI.closeResults();
        this._emit('currentLocationSelected');
    }

    // Handler für das UI-Event "onSearchEnteredAddress"
    _handleSearchEnteredAddressFromUI(query) {
        console.log("DEBUG (Coordinator): 'Zur eingegebenen Adresse' Button vom UI geklickt, starte normale Suche.");
        // Führt die Suche aus und emittiert das spezifische Event
        this.handleSearchInput(query);
        this._emit('searchEnteredAddress', query);
    }

    /**
     * Handler für das UI-Event "stateChange".
     * Löst ein Coordinator-Event "sheetStateChange" aus.
     * @param {string} newState - Der neue Zustand des UI-Sheets.
     * @private
     */
    _handleStateChangeFromUI(newState) {
        console.log(`DEBUG (Coordinator): UI-Sheet Zustand geändert zu: ${newState}, gebe "sheetStateChange" Event weiter.`);
        this._emit('sheetStateChange', newState);
    }

    /**
     * Handler für das UI-Event "sheetTriggered".
     * Löst ein Coordinator-Event "sheetOpened" aus.
     * @private
     */
    _handleSheetTriggeredFromUI() {
        console.log("DEBUG (Coordinator): Sheet Trigger vom UI empfangen, gebe 'sheetOpened' Event weiter.");
        this._emit('sheetOpened');
    }

    /**
     * Registriert einen Callback für das "addressSelectedForMap"-Event.
     * @param {function({lat: number, lon: number, displayName: string, source: string}): void} callback
     */
    onAddressSelected(callback) {
        this.addressSelectedCallbacks.push(callback);
    }

    /**
     * Registriert einen Callback für das "currentLocationSelected"-Event.
     * @param {function(): void} callback
     */
    onCurrentLocationSelected(callback) {
        this.currentLocationSelectedCallbacks.push(callback);
    }

    /**
     * Registriert einen Callback für das "searchEnteredAddress"-Event.
     * @param {function(string): void} callback
     */
    onSearchEnteredAddress(callback) {
        this.searchEnteredAddressCallbacks.push(callback);
    }

    /**
     * Registriert einen Callback für das "sheetStateChange"-Event.
     * @param {function(string): void} callback
     */
    onSheetStateChange(callback) {
        this.stateChangeCallbacks.push(callback);
    }

    /**
     * Registriert einen Callback für das "sheetOpened"-Event.
     * @param {function(): void} callback
     */
    onSheetOpened(callback) {
        this.sheetTriggeredCallbacks.push(callback);
    }

    /**
     * Hilfsfunktion zum Auslösen von benutzerdefinierten Events.
     * @param {string} eventName - Name des auszulösenden Events.
     * @param {*} [data] - Daten, die mit dem Event übergeben werden sollen.
     * @private
     */
    _emit(eventName, data) {
        const callbacks = {
            'addressSelectedForMap': this.addressSelectedCallbacks,
            'currentLocationSelected': this.currentLocationSelectedCallbacks,
            'sheetStateChange': this.stateChangeCallbacks,
            'sheetOpened': this.sheetTriggeredCallbacks,
            'searchEnteredAddress': this.searchEnteredAddressCallbacks // Korrekt
        };

        if (callbacks[eventName]) {
            callbacks[eventName].forEach(cb => {
                try {
                    cb(data);
                } catch (e) {
                    console.error(`Fehler beim Ausführen des Event-Listeners für '${eventName}':`, e);
                }
            });
        }
    }

    // ====================================================================
    // HILFSFUNKTIONEN FÜR POI-NAMENSSUCHE (Name/Jahr)
    // ====================================================================

    /**
     * Extrahiert Namensbestandteile (mind. 1 Komponente) 
     * und ein optionales 4-stelliges Geburtsjahr aus der Query.
     * @param {string} query Die rohe Benutzereingabe (z.B. "Max-Emil Mustermann geb. 1900").
     * @returns {{nameParts: string[], yearOfBirth: number|null}}
     */
  
    _extractNameComponents(query) {
        let yearOfBirth = null;

        const rawParts = query
            .split(/\s+/)
            .map(p => p.trim())
            .filter(Boolean);

        const nameParts = [];
        const nameRegex = /^[a-zA-ZäöüÄÖÜß\-]+$/;

        for (const token of rawParts) {

            // A) gültiges Jahr
            if (/^\d{4}$/.test(token)) {
                const yr = parseInt(token, 10);
                if (yr >= 1840 && yr <= 1945) {
                    yearOfBirth = yearOfBirth ?? yr;
                    continue;
                }
                // Jahr 4-stellig, aber ungültig → Abbruch
                return { invalid: true, nameParts: [], yearOfBirth: null };
            }

            // B) gültiger alphabetischer Name
            if (nameRegex.test(token)) {
                nameParts.push(token);
                continue;
            }

            // ❌ C) alles andere → Abbruch
            return { invalid: true, nameParts: [], yearOfBirth: null };
        }

        // Jetzt sind nur gültige Tokens enthalten.
        return { invalid: false, nameParts, yearOfBirth };
    }


    // ====================================================================
    // VORHANDENE HILFSFUNKTIONEN FÜR ADRESSSUCHE
    // ====================================================================

    /**
     * Versucht, eine Adresse (Straße, Hausnummer) aus einer Freitext-Abfrage zu extrahieren.
     * @param {string} query Die Freitext-Abfrage (z.B. "Alexanderstraße 6-7, Mitte").
     * @returns {{street: string | null, houseNr: string | null}}
     * @private
     */
    _extractAddressComponents(query) {


        const components = { street: null, houseNr: null };
        const streetAndNumberPart = query.split(',')[0].trim();

        // Regex für Hausnummer
        const houseNrRegex = /\s(\d{1,5}[a-zA-Z]?(?:-\d{1,5}[a-zA-Z]?)?)$/;
        const match = streetAndNumberPart.match(houseNrRegex);

        if (match) {
            components.houseNr = match[1].trim();
            components.street = streetAndNumberPart.substring(0, match.index).trim();
        } else {
            components.street = streetAndNumberPart.trim();
        }

        console.log("DEBUG: Extrahierte Adresskomponenten für POI-Backend:", components);
        return components;
    }

    

   
    // ====================================================================
    // HAUPTLOGIK: handleSearchInput (Jetzt mit POI Name/Jahr Priorität)
    // ====================================================================

    async handleSearchInput(query) {
        if (query.length < 3) {
            clearTimeout(this.searchTimeout);
            this.addressSheetUI.setInitialResultsContent();
            return;
        }

        clearTimeout(this.searchTimeout);

        this.searchTimeout = setTimeout(async () => {

            // Initialer Status: Suchen...
            //           this.addressSheetUI.renderResults([{ display_name: '<p style="text-align: center; color: #888; padding: 20px;"><i class="fas fa-spinner fa-spin"></i> Suchen...</p>', source: 'status', lat: 0, lon: 0 }]);
            const html = '<p style="--status-color: #888; --status-padding: 20px; text-align: center; color: var(--status-color); padding: var(--status-padding);"><i class="fas fa-spinner fa-spin"></i> Suchen...</p>';

            this.addressSheetUI.renderResults([
                { display_name: html, source: 'status', lat: 0, lon: 0 }
            ]);
            const hausnummerRegex = /[.\s]\d{1,3}[a-zA-Z]?/;
            const ishouseNrQuery = hausnummerRegex.test(query);

            let poiNameResults = [];
            let geoResults = [];

            // =========================================================
            // 1. POI-DB nach Name/Jahr abfragen (höchste Priorität)
            // =========================================================
            const nameComponents = this._extractNameComponents(query);
            const { nameParts, yearOfBirth, invalid } = nameComponents;

            // 1) Harte Validierung aus dem Extraktor
            if (invalid) {
                console.log("DEBUG: Ungültige Eingabe erkannt. Abbruch.");
                //this._showUserError("Eingabe ungültig: Bitte nur Namen und ein Jahr (1840–1945) eingeben. Keine Hausnummern oder Mischtexte.");
                //return;
            } else {

                // 2) Jahr prüfen
                const hasYear = yearOfBirth !== null;
                const isValidYear = Number.isInteger(yearOfBirth) &&
                    yearOfBirth >= 1840 &&
                    yearOfBirth <= 1945;

                // 3) Bedingungen wie im Backend
                const caseA = (nameParts.length > 0 && isValidYear);  // 1+ Namen + gültiges Jahr
                const caseB = (nameParts.length > 1 && !hasYear);      // 2+ Namen ohne Jahr

                const shouldCallAPI = caseA || caseB;

                if (shouldCallAPI) {
                    try {
                        poiNameResults = await this.backendService.fetchPoisByName(nameParts, yearOfBirth);
                        console.log(`DEBUG: POI-Backend (Name/Jahr) hat ${poiNameResults.length} Treffer gefunden.`);
                    } catch (e) {
                        console.error("POI-Suche (Name/Jahr) fehlgeschlagen:", e.message);
                    }
                } else {
                    console.log("DEBUG: POI-Suche übersprungen (Regeln nicht erfüllt).", {
                        nameParts,
                        yearOfBirth,
                        caseA,
                        caseB
                    });
                }
            }



            // =========================================================
            // 2. ZUSÄTZLICH Geolocation Backends suchen (Adresse/POI-Adresse)
            // =========================================================
            try {
                // 2a. Nominatim-Suche starten (immer)
                const nominatimResults = await fetchAdressesFromNominatim(query);
                geoResults = nominatimResults;

                // Prüfen, ob eine Hausnummer in der Query ist UND Nominatim keine gefunden hat
                const nominatimFoundHouseNr = nominatimResults.some(item => item.houseNr && item.source === 'OSM');

                if (ishouseNrQuery && !nominatimFoundHouseNr) {

                    // Status: Spezifische Suche (HERE)...
                    this.addressSheetUI.renderResults([{ display_name: '<p style="text-align: center; color: #888; padding: 20px;"><i class="fas fa-search-location"></i> Spezifische Suche (HERE)...</p>', source: 'status', lat: 0, lon: 0 }]);

                    // 2b. HERE-Fallback-Check
                    const hereResults = await fetchAdressesFromHere(query);

                    if (hereResults && hereResults.length > 0) {
                        // HERE-Ergebnisse werden Geolocation-Ergebnissen vorangestellt
                        geoResults = [...hereResults, ...nominatimResults];
                    } else {
                        // 2c. POI-Address-Fallback-Check (wenn HERE/Nominatim fehlschlagen)
                        const addressComponents = this._extractAddressComponents(query);

                        if (addressComponents.street && addressComponents.houseNr) {
                            // Status: POI-Datenbank Suche (Adresse)...
                            this.addressSheetUI.renderResults([{ display_name: '<p style="text-align: center; color: #888; padding: 20px;"><i class="fas fa-database"></i> Suche in POI-Datenbank (Adresse)...</p>', source: 'status', lat: 0, lon: 0 }]);

                            const poiAddressResults = await this.backendService.fetchPoisByAddress(
                                addressComponents.street,
                                addressComponents.houseNr
                            );

                            if (poiAddressResults && poiAddressResults.length > 0) {
                                console.log(`DEBUG: POI-Backend (Adresse) hat ${poiAddressResults.length} Treffer gefunden.`);
                                // Füge POI-Address-Ergebnisse zu den geoResults hinzu
                                geoResults = [...poiAddressResults, ...geoResults];
                            }
                        }
                    }
                }
            } catch (e) {
                console.error("Geolocation-Suche fehlgeschlagen:", e.message);
            }

            // =========================================================
            // ERGEBNISSE KOMBINIEREN & RENDERN
            // =========================================================
            let finalResults = [];

            // POI Name/Jahr Ergebnisse zuerst hinzufügen (hohe Prio)
            if (poiNameResults.length > 0) {
                finalResults = [...poiNameResults];
            }

            // Geolocation (inkl. Nominatim, HERE, und ggf. POI Adresse) Ergebnisse hinzufügen
            if (geoResults.length > 0) {
                // Duplikatprüfung wäre hier sinnvoll, falls POI-Adresse und POI-Name/Jahr das Gleiche finden, 
                // aber für dieses Beispiel lassen wir die Ergebnisse einfach zusammenlaufen.
                finalResults = [...finalResults, ...geoResults];
            }

            // Anzeige
            if (finalResults.length > 0) {
                this.addressSheetUI.renderResults(finalResults);
            } else {
                console.log("DEBUG: Alle Suchen waren erfolglos.");
                this.addressSheetUI.renderResults([]);
                showToast(`Keine Treffer für "${query}" gefunden.`, 'Information');
            }

        }, 500);
    }
}