// loc-tracking-co.js
import { showToast, mylog } from './sc-utilities.js';

/**
 * Eine Klasse zur Verwaltung des Benutzerstandorts, implementiert als Singleton.
 * Übernimmt die Logik des Geolocation-Trackings und dient als Dispatcher.
 * Enthält KEINE Logik zur Kartenvisualisierung (View-Trennung!).
 * */
export class LocationTrackingCoordinator {
    // Statische private Variable, die die Singleton-Instanz speichert
    /** @private {LocationTrackingCoordinator|null} */
    static #instance = null;

    static getInstance() { 
        if (!LocationTrackingCoordinator.#instance) {
            LocationTrackingCoordinator.#instance = new LocationTrackingCoordinator();
        }
        return LocationTrackingCoordinator.#instance;
    }
    
    /**
     * @private {boolean} _isTrackingActive - Status, ob das Tracking aktiv ist.
     * @private {number|null} _watchId - ID des watchPosition-callbacks, falls aktiv.
     * @private {object|null} _lastKnownPosition - Die zuletzt erfolgreich abgerufene GeolocationPosition (Das Model/die Daten).
     * @private {Array<function(boolean)>} _trackingStateChangeListeners - Liste der Callbacks für Statusänderungen (für UI-Toggle/Settings-View).
     * @private {Array<function(GeolocationPosition, boolean)>} _newPositionListeners - Liste der Callbacks für neue Positionsdaten (für Map View).
     */
    
    /**
     * @private
     * Privater Konstruktor, um die direkte Instanziierung zu verhindern.
     */
    constructor() {
        if (LocationTrackingCoordinator.#instance) {
             throw new Error("Verwenden Sie #locationTracker = LocationTrackingCoordinator.getInstance(); statt new LocationTrackingCoordinator().");
        }

        this._isManuallyDeactivatedByUser = false;
        this._trackingActive = false;
        this._watchId = null;
        this._lastKnownPosition = null;
        this._trackingStateChangeListeners = [];
        this._newPositionListeners = []; 
        
        mylog("LocationTrackingCoordinator: Singleton-Instanziiert.");
    }
    
    // --- Observer / Dispatcher Methoden ---
    
    // ermöglicht den Abonnenten (zB SettingsUI), auf den Wechsel bei der 
    // Standortverfolgung zu reagieren, indem sie ihre Listener-Funktion in die Liste
    // _trackingStateChangeListeners eintragen
    onTrackingStateChange(callback) {
        if (typeof callback === 'function') {
            this._trackingStateChangeListeners.push(callback);
        }
    }
    
    // NEU: Spezifisch für Positionsupdates (abonniert von MapLocationUI)
    onNewPosition(callback) { 
        if (typeof callback === 'function') {
            this._newPositionListeners.push(callback);
        }
    }
// dispatcher funktionen, die den listenern / abonnenten benachrichtigen
// grundsätzlich können mehrer listener bedient werden (forEach)
    /**
     * 
     * @private
     * @param {boolean} newState - Der neue Tracking-Zustand.
     */
    _dispatchTrackingState(newState) {
        this._trackingStateChangeListeners.forEach(callback => {
            try {
                callback(newState);
            } catch (e) {
                console.error("Fehler beim Ausführen des TrackingStateChangeListeners:", e);
            }
        });
        mylog(`LocationTrackingCoordinator: Tracking-Status geändert zu: ${newState ? 'aktiv' : 'inaktiv'}`);
    }

    // Dispatcher für neue Positionen (für Map View)
    _dispatchNewPosition(position) {
        this._lastKnownPosition = position;
        this._newPositionListeners.forEach(callback => {
            try {
                callback(position, false); // false = nicht initial, Map View soll nicht zentrieren
            } catch (e) {
                console.error("Fehler beim Ausführen des NewPositionListeners:", e);
            }
        });
    }

    // Dispatcher für initiale Position (Map View soll sich zentrieren)
    _dispatchInitialPosition(position) {
        this._lastKnownPosition = position;
        this._newPositionListeners.forEach(callback => {
            try {
                callback(position, true); // true = initiale Position, Map View soll zentrieren
            } catch (e) {
                console.error("Fehler beim Ausführen des InitialPositionListeners:", e);
            }
        });
    }

    // Ändert bei Statuswechsel 1. die interne Statusvariable this._trackingActive  
    // und informiert 2. gleichzeitig die Listener.
    _setTrackingActive(newState) {
        if (this._trackingActive !== newState) {
            this._trackingActive = newState;
            this._dispatchTrackingState(newState); // Aufruf korrigiert
        }
    }
    
    // --- Model Query Methoden ---
    
    isTrackingActive() {
        return this._trackingActive && !this._isManuallyDeactivatedByUser;
    }

    isManuallyDeactivated() {
        return this._isManuallyDeactivatedByUser;
    }

    getUserPosition() {
        if (this._lastKnownPosition) {
            mylog("LocationTrackingCoordinator: getUserPosition aufgerufen. Letzte bekannte Position:", this._lastKnownPosition.coords.latitude, this._lastKnownPosition.coords.longitude);
            return [this._lastKnownPosition.coords.latitude, this._lastKnownPosition.coords.longitude];
        }
        mylog("LocationTrackingCoordinator: getUserPosition aufgerufen. Keine bekannte Position vorhanden.");
        return null;
    }

    // --- Controller Methoden ---

    /**
     * Startet den Geolocation-Watch-Loop.
     * @private
     * @param {GeolocationPosition} position - Die initial abgerufene Position.
     */
    _startWatchPosition(initialPosition) { 
        if (this._trackingActive) return;

        this._setTrackingActive(true);
        
        this._dispatchInitialPosition(initialPosition); 

        this._watchId = navigator.geolocation.watchPosition(
            (pos) => this._dispatchNewPosition(pos), 
            (err) => this._handleGeolocationError(err),
            { enableHighAccuracy: true, timeout: 3000, maximumAge: 0 }
        );
        mylog("LocationTrackingCoordinator: watchPosition-Tracking gestartet.");
    }

    _stopTracking() {
        if (this._watchId !== null) {
            navigator.geolocation.clearWatch(this._watchId);
            this._watchId = null;
            this._setTrackingActive(false);

            this._lastKnownPosition = null;
        
            mylog("LocationTrackingCoordinator: Standort-Tracking beendet und Position geleert.");
        }
    }

    _handleGeolocationError(error) {
        let message = '';
        let type = 'Fehler';
        switch (error.code) {
             case GeolocationPositionError.PERMISSION_DENIED:
                message = 'Standortzugriff verweigert.';
                this._stopTracking();
                break;
            case GeolocationPositionError.POSITION_UNAVAILABLE:
                message = 'Standortinformationen sind nicht verfügbar.';
                this._stopTracking();
                break;
            case GeolocationPositionError.TIMEOUT:
                if (this._trackingActive) {
                    message = 'Standort konnte nicht aktualisiert werden.';
                    type = 'Warnung';
                } else {
                    message = 'Zeitüberschreitung beim Abrufen des Standorts.';
                    this._stopTracking();
                }
                break;
            default:
                message = `Ein unbekannter Fehler ist aufgetreten: ${error.message || 'Details unbekannt'}`;
                this._stopTracking();
                break;
        }
        showToast(message, type);

        if (typeof this._resolvePromise === 'function') {
            this._resolvePromise(false);
            this._resolvePromise = null;
        }
    }
    
    /**
     * Öffentliche Methode, die den Prozess des Abrufs der Berechtigung und des Starts des Trackings kapselt.
     *
     * @returns {Promise<boolean>} Ein Promise, das mit `true` aufgelöst wird, wenn das Tracking gestartet wurde,
     * und mit `false`, wenn es fehlschlägt oder die Berechtigung verweigert wird.
     */
    async startTrackingAndHandlePermission() {
        mylog("LocationTrackingCoordinator: starte Tracking-Prozess...");
        this._isManuallyDeactivatedByUser = false;

        if (!navigator.geolocation) {
            showToast('Geolocation wird von Ihrem Browser nicht unterstützt.', 'Fehler');
            this._setTrackingActive(false);
            return false;
        }

        try {
            // Schritt 1: Geolocation-Berechtigungsstatus prüfen
            const permissionStatus = await navigator.permissions.query({ name: 'geolocation' });

            if (permissionStatus.state === 'denied') {
                // Schritt 2: Wenn verweigert, sofort Fehlermeldung anzeigen und abbrechen
                this._handleGeolocationError({
                    code: 1, 
                    message: "Standortzugriff wurde dauerhaft verweigert."
                });
                return false;
            }

            // Schritt 3: Normale Abfrage starten
            return new Promise(resolve => {
                navigator.geolocation.getCurrentPosition(
                    (position) => {
                        mylog("Geolocation: Initialer Abruf erfolgreich.");
                        this._startWatchPosition(position);
                        resolve(true); 
                    },
                    (error) => {
                        this._handleGeolocationError(error);
                        resolve(false); 
                    },
                    {
                        enableHighAccuracy: true,
                        timeout: 5000,
                        maximumAge: 0
                    }
                );
            });

        } catch (e) {
            console.error("Fehler beim Abfragen des Berechtigungsstatus:", e);
            showToast('Fehler bei der Berechtigungsabfrage.', 'Fehler');
            return false;
        }
    }

    stopLocationTracking() {
        mylog("LocationTrackingCoordinator: stopLocationTracking aufgerufen.");
        this._isManuallyDeactivatedByUser = true;
        this._stopTracking();
    }
}