/**
 * ThrillWiki Geolocation - User Location and "Near Me" Functionality
 * 
 * This module handles browser geolocation API integration with privacy-conscious
 * permission handling, distance calculations, and "near me" functionality
 */

class UserLocation {
    constructor(options = {}) {
        this.options = {
            enableHighAccuracy: true,
            timeout: 10000,
            maximumAge: 300000, // 5 minutes
            watchPosition: false,
            autoShowOnMap: true,
            showAccuracyCircle: true,
            enableCaching: true,
            cacheKey: 'thrillwiki_user_location',
            apiEndpoints: {
                nearby: '/api/map/nearby/',
                distance: '/api/map/distance/'
            },
            defaultRadius: 50, // miles
            maxRadius: 500,
            ...options
        };
        
        this.currentPosition = null;
        this.watchId = null;
        this.mapInstance = null;
        this.locationMarker = null;
        this.accuracyCircle = null;
        this.permissionState = 'unknown';
        this.lastLocationTime = null;
        
        // Event handlers
        this.eventHandlers = {
            locationFound: [],
            locationError: [],
            permissionChanged: []
        };
        
        this.init();
    }
    
    /**
     * Initialize the geolocation component
     */
    init() {
        this.checkGeolocationSupport();
        this.loadCachedLocation();
        this.setupLocationButtons();
        this.checkPermissionState();
    }
    
    /**
     * Check if geolocation is supported by the browser
     */
    checkGeolocationSupport() {
        if (!navigator.geolocation) {
            console.warn('Geolocation is not supported by this browser');
            this.hideLocationButtons();
            return false;
        }
        return true;
    }
    
    /**
     * Setup location-related buttons and controls
     */
    setupLocationButtons() {
        // Find all "locate me" buttons
        const locateButtons = document.querySelectorAll('[data-action="locate-user"], .locate-user-btn');
        
        locateButtons.forEach(button => {
            button.addEventListener('click', (e) => {
                e.preventDefault();
                this.requestLocation();
            });
        });
        
        // Find "near me" buttons
        const nearMeButtons = document.querySelectorAll('[data-action="near-me"], .near-me-btn');
        
        nearMeButtons.forEach(button => {
            button.addEventListener('click', (e) => {
                e.preventDefault();
                this.showNearbyLocations();
            });
        });
        
        // Distance calculator buttons
        const distanceButtons = document.querySelectorAll('[data-action="calculate-distance"]');
        
        distanceButtons.forEach(button => {
            button.addEventListener('click', (e) => {
                e.preventDefault();
                const targetLat = parseFloat(button.dataset.lat);
                const targetLng = parseFloat(button.dataset.lng);
                this.calculateDistance(targetLat, targetLng);
            });
        });
    }
    
    /**
     * Hide location buttons when geolocation is not supported
     */
    hideLocationButtons() {
        const locationElements = document.querySelectorAll('.geolocation-feature');
        locationElements.forEach(el => {
            el.style.display = 'none';
        });
    }
    
    /**
     * Check current permission state
     */
    async checkPermissionState() {
        if ('permissions' in navigator) {
            try {
                const permission = await navigator.permissions.query({ name: 'geolocation' });
                this.permissionState = permission.state;
                this.updateLocationButtonStates();
                
                // Listen for permission changes
                permission.addEventListener('change', () => {
                    this.permissionState = permission.state;
                    this.updateLocationButtonStates();
                    this.triggerEvent('permissionChanged', this.permissionState);
                });
            } catch (error) {
                console.warn('Could not check geolocation permission:', error);
            }
        }
    }
    
    /**
     * Update location button states based on permission
     */
    updateLocationButtonStates() {
        const locateButtons = document.querySelectorAll('[data-action="locate-user"], .locate-user-btn');
        
        locateButtons.forEach(button => {
            const icon = button.querySelector('i') || button;
            
            switch (this.permissionState) {
                case 'granted':
                    button.disabled = false;
                    button.title = 'Find my location';
                    icon.className = 'fas fa-crosshairs';
                    break;
                case 'denied':
                    button.disabled = true;
                    button.title = 'Location access denied';
                    icon.className = 'fas fa-times-circle';
                    break;
                case 'prompt':
                default:
                    button.disabled = false;
                    button.title = 'Find my location (permission required)';
                    icon.className = 'fas fa-crosshairs';
                    break;
            }
        });
    }
    
    /**
     * Request user location
     */
    requestLocation(options = {}) {
        if (!navigator.geolocation) {
            this.handleLocationError(new Error('Geolocation not supported'));
            return;
        }
        
        const requestOptions = {
            ...this.options,
            ...options
        };
        
        // Show loading state
        this.setLocationButtonLoading(true);
        
        navigator.geolocation.getCurrentPosition(
            (position) => this.handleLocationSuccess(position),
            (error) => this.handleLocationError(error),
            {
                enableHighAccuracy: requestOptions.enableHighAccuracy,
                timeout: requestOptions.timeout,
                maximumAge: requestOptions.maximumAge
            }
        );
    }
    
    /**
     * Start watching user position
     */
    startWatching() {
        if (!navigator.geolocation || this.watchId) return;
        
        this.watchId = navigator.geolocation.watchPosition(
            (position) => this.handleLocationSuccess(position),
            (error) => this.handleLocationError(error),
            {
                enableHighAccuracy: this.options.enableHighAccuracy,
                timeout: this.options.timeout,
                maximumAge: this.options.maximumAge
            }
        );
    }
    
    /**
     * Stop watching user position
     */
    stopWatching() {
        if (this.watchId) {
            navigator.geolocation.clearWatch(this.watchId);
            this.watchId = null;
        }
    }
    
    /**
     * Handle successful location acquisition
     */
    handleLocationSuccess(position) {
        this.currentPosition = {
            lat: position.coords.latitude,
            lng: position.coords.longitude,
            accuracy: position.coords.accuracy,
            timestamp: position.timestamp
        };
        
        this.lastLocationTime = Date.now();
        
        // Cache location
        if (this.options.enableCaching) {
            this.cacheLocation(this.currentPosition);
        }
        
        // Show on map if enabled
        if (this.options.autoShowOnMap && this.mapInstance) {
            this.showLocationOnMap();
        }
        
        // Update button states
        this.setLocationButtonLoading(false);
        this.updateLocationButtonStates();
        
        // Trigger event
        this.triggerEvent('locationFound', this.currentPosition);
        
        console.log('Location found:', this.currentPosition);
    }
    
    /**
     * Handle location errors
     */
    handleLocationError(error) {
        this.setLocationButtonLoading(false);
        
        let message = 'Unable to get your location';
        
        switch (error.code) {
            case error.PERMISSION_DENIED:
                message = 'Location access denied. Please enable location services.';
                this.permissionState = 'denied';
                break;
            case error.POSITION_UNAVAILABLE:
                message = 'Location information is unavailable.';
                break;
            case error.TIMEOUT:
                message = 'Location request timed out.';
                break;
            default:
                message = 'An unknown error occurred while retrieving location.';
                break;
        }
        
        this.showLocationMessage(message, 'error');
        this.updateLocationButtonStates();
        
        // Trigger event
        this.triggerEvent('locationError', { error, message });
        
        console.error('Location error:', error);
    }
    
    /**
     * Show user location on map
     */
    showLocationOnMap() {
        if (!this.mapInstance || !this.currentPosition) return;
        
        const { lat, lng, accuracy } = this.currentPosition;
        
        // Remove existing location marker and circle
        this.clearLocationDisplay();
        
        // Add location marker
        this.locationMarker = L.marker([lat, lng], {
            icon: this.createUserLocationIcon()
        }).addTo(this.mapInstance);
        
        this.locationMarker.bindPopup(`
            <div class="user-location-popup">
                <h4><i class="fas fa-map-marker-alt"></i> Your Location</h4>
                <p class="accuracy">Accuracy: ±${Math.round(accuracy)}m</p>
                <div class="location-actions">
                    <button onclick="userLocation.showNearbyLocations()" class="btn btn-primary btn-sm">
                        <i class="fas fa-search"></i> Find Nearby Parks
                    </button>
                </div>
            </div>
        `);
        
        // Add accuracy circle if enabled and accuracy is reasonable
        if (this.options.showAccuracyCircle && accuracy < 1000) {
            this.accuracyCircle = L.circle([lat, lng], {
                radius: accuracy,
                fillColor: '#3388ff',
                fillOpacity: 0.2,
                color: '#3388ff',
                weight: 2,
                opacity: 0.5
            }).addTo(this.mapInstance);
        }
        
        // Center map on user location
        this.mapInstance.setView([lat, lng], 13);
    }
    
    /**
     * Create custom icon for user location
     */
    createUserLocationIcon() {
        return L.divIcon({
            className: 'user-location-marker',
            html: `
                <div class="user-location-inner">
                    <i class="fas fa-crosshairs"></i>
                </div>
            `,
            iconSize: [24, 24],
            iconAnchor: [12, 12]
        });
    }
    
    /**
     * Clear location display from map
     */
    clearLocationDisplay() {
        if (this.locationMarker && this.mapInstance) {
            this.mapInstance.removeLayer(this.locationMarker);
            this.locationMarker = null;
        }
        
        if (this.accuracyCircle && this.mapInstance) {
            this.mapInstance.removeLayer(this.accuracyCircle);
            this.accuracyCircle = null;
        }
    }
    
    /**
     * Show nearby locations
     */
    async showNearbyLocations(radius = null) {
        if (!this.currentPosition) {
            this.requestLocation();
            return;
        }
        
        try {
            const searchRadius = radius || this.options.defaultRadius;
            const { lat, lng } = this.currentPosition;
            
            const params = new URLSearchParams({
                lat: lat,
                lng: lng,
                radius: searchRadius,
                unit: 'miles'
            });
            
            const response = await fetch(`${this.options.apiEndpoints.nearby}?${params}`);
            const data = await response.json();
            
            if (data.status === 'success') {
                this.displayNearbyResults(data.data);
            } else {
                this.showLocationMessage('No nearby locations found', 'info');
            }
        } catch (error) {
            console.error('Failed to find nearby locations:', error);
            this.showLocationMessage('Failed to find nearby locations', 'error');
        }
    }
    
    /**
     * Display nearby search results
     */
    displayNearbyResults(results) {
        // Find or create results container
        let resultsContainer = document.getElementById('nearby-results');
        
        if (!resultsContainer) {
            resultsContainer = document.createElement('div');
            resultsContainer.id = 'nearby-results';
            resultsContainer.className = 'nearby-results-container';
            
            // Try to insert after a logical element
            const mapContainer = document.getElementById('map-container');
            if (mapContainer && mapContainer.parentNode) {
                mapContainer.parentNode.insertBefore(resultsContainer, mapContainer.nextSibling);
            } else {
                document.body.appendChild(resultsContainer);
            }
        }
        
        const html = `
            <div class="nearby-results">
                <h3 class="results-title">
                    <i class="fas fa-map-marker-alt"></i>
                    Nearby Parks (${results.length} found)
                </h3>
                <div class="results-list">
                    ${results.map(location => `
                        <div class="nearby-item">
                            <div class="location-info">
                                <h4 class="location-name">${location.name}</h4>
                                <p class="location-address">${location.formatted_location || ''}</p>
                                <p class="location-distance">
                                    <i class="fas fa-route"></i>
                                    ${location.distance} away
                                </p>
                            </div>
                            <div class="location-actions">
                                <button onclick="userLocation.centerOnLocation(${location.latitude}, ${location.longitude})" 
                                        class="btn btn-outline btn-sm">
                                    <i class="fas fa-map"></i> Show on Map
                                </button>
                            </div>
                        </div>
                    `).join('')}
                </div>
            </div>
        `;
        
        resultsContainer.innerHTML = html;
        
        // Scroll to results
        resultsContainer.scrollIntoView({ behavior: 'smooth' });
    }
    
    /**
     * Calculate distance to a specific location
     */
    async calculateDistance(targetLat, targetLng) {
        if (!this.currentPosition) {
            this.showLocationMessage('Please enable location services first', 'warning');
            return null;
        }
        
        try {
            const { lat, lng } = this.currentPosition;
            
            const params = new URLSearchParams({
                from_lat: lat,
                from_lng: lng,
                to_lat: targetLat,
                to_lng: targetLng
            });
            
            const response = await fetch(`${this.options.apiEndpoints.distance}?${params}`);
            const data = await response.json();
            
            if (data.status === 'success') {
                return data.data;
            }
        } catch (error) {
            console.error('Failed to calculate distance:', error);
        }
        
        // Fallback to Haversine formula
        return this.calculateHaversineDistance(
            this.currentPosition.lat,
            this.currentPosition.lng,
            targetLat,
            targetLng
        );
    }
    
    /**
     * Calculate distance using Haversine formula
     */
    calculateHaversineDistance(lat1, lng1, lat2, lng2) {
        const R = 3959; // Earth's radius in miles
        const dLat = this.toRadians(lat2 - lat1);
        const dLng = this.toRadians(lng2 - lng1);
        
        const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                  Math.cos(this.toRadians(lat1)) * Math.cos(this.toRadians(lat2)) *
                  Math.sin(dLng / 2) * Math.sin(dLng / 2);
        
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        const distance = R * c;
        
        return {
            distance: Math.round(distance * 10) / 10,
            unit: 'miles'
        };
    }
    
    /**
     * Convert degrees to radians
     */
    toRadians(degrees) {
        return degrees * (Math.PI / 180);
    }
    
    /**
     * Center map on specific location
     */
    centerOnLocation(lat, lng, zoom = 15) {
        if (this.mapInstance) {
            this.mapInstance.setView([lat, lng], zoom);
        }
    }
    
    /**
     * Cache user location
     */
    cacheLocation(position) {
        try {
            const cacheData = {
                position: position,
                timestamp: Date.now()
            };
            localStorage.setItem(this.options.cacheKey, JSON.stringify(cacheData));
        } catch (error) {
            console.warn('Failed to cache location:', error);
        }
    }
    
    /**
     * Load cached location
     */
    loadCachedLocation() {
        if (!this.options.enableCaching) return null;
        
        try {
            const cached = localStorage.getItem(this.options.cacheKey);
            if (!cached) return null;
            
            const cacheData = JSON.parse(cached);
            const age = Date.now() - cacheData.timestamp;
            
            // Check if cache is still valid (5 minutes)
            if (age < this.options.maximumAge) {
                this.currentPosition = cacheData.position;
                this.lastLocationTime = cacheData.timestamp;
                return this.currentPosition;
            } else {
                // Remove expired cache
                localStorage.removeItem(this.options.cacheKey);
            }
        } catch (error) {
            console.warn('Failed to load cached location:', error);
        }
        
        return null;
    }
    
    /**
     * Set loading state for location buttons
     */
    setLocationButtonLoading(loading) {
        const locateButtons = document.querySelectorAll('[data-action="locate-user"], .locate-user-btn');
        
        locateButtons.forEach(button => {
            const icon = button.querySelector('i') || button;
            
            if (loading) {
                button.disabled = true;
                icon.className = 'fas fa-spinner fa-spin';
            } else {
                button.disabled = false;
                // Icon will be updated by updateLocationButtonStates
            }
        });
    }
    
    /**
     * Show location-related message
     */
    showLocationMessage(message, type = 'info') {
        // Create or update message element
        let messageEl = document.getElementById('location-message');
        
        if (!messageEl) {
            messageEl = document.createElement('div');
            messageEl.id = 'location-message';
            messageEl.className = 'location-message';
            
            // Insert at top of page or after header
            const header = document.querySelector('header, .header');
            if (header) {
                header.parentNode.insertBefore(messageEl, header.nextSibling);
            } else {
                document.body.insertBefore(messageEl, document.body.firstChild);
            }
        }
        
        messageEl.textContent = message;
        messageEl.className = `location-message location-message-${type}`;
        messageEl.style.display = 'block';
        
        // Auto-hide after delay
        setTimeout(() => {
            if (messageEl.parentNode) {
                messageEl.style.display = 'none';
            }
        }, 5000);
    }
    
    /**
     * Connect to a map instance
     */
    connectToMap(mapInstance) {
        this.mapInstance = mapInstance;
        
        // Show cached location on map if available
        if (this.currentPosition && this.options.autoShowOnMap) {
            this.showLocationOnMap();
        }
    }
    
    /**
     * Get current position
     */
    getCurrentPosition() {
        return this.currentPosition;
    }
    
    /**
     * Check if location is available
     */
    hasLocation() {
        return this.currentPosition !== null;
    }
    
    /**
     * Check if location is recent
     */
    isLocationRecent(maxAge = 300000) { // 5 minutes default
        if (!this.lastLocationTime) return false;
        return (Date.now() - this.lastLocationTime) < maxAge;
    }
    
    /**
     * Add event listener
     */
    on(event, handler) {
        if (!this.eventHandlers[event]) {
            this.eventHandlers[event] = [];
        }
        this.eventHandlers[event].push(handler);
    }
    
    /**
     * Remove event listener
     */
    off(event, handler) {
        if (this.eventHandlers[event]) {
            const index = this.eventHandlers[event].indexOf(handler);
            if (index > -1) {
                this.eventHandlers[event].splice(index, 1);
            }
        }
    }
    
    /**
     * Trigger event
     */
    triggerEvent(event, data) {
        if (this.eventHandlers[event]) {
            this.eventHandlers[event].forEach(handler => {
                try {
                    handler(data);
                } catch (error) {
                    console.error(`Error in ${event} handler:`, error);
                }
            });
        }
    }
    
    /**
     * Destroy the geolocation instance
     */
    destroy() {
        this.stopWatching();
        this.clearLocationDisplay();
        this.eventHandlers = {};
    }
}

// Auto-initialize user location
document.addEventListener('DOMContentLoaded', function() {
    window.userLocation = new UserLocation();
    
    // Connect to map instance if available
    if (window.thrillwikiMap) {
        window.userLocation.connectToMap(window.thrillwikiMap);
    }
});

// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
    module.exports = UserLocation;
} else {
    window.UserLocation = UserLocation;
}