/**
 * ThrillWiki Maps - Core Map Functionality
 * 
 * This module provides the main map functionality for ThrillWiki using Leaflet.js
 * Includes clustering, filtering, dark mode support, and HTMX integration
 */

class ThrillWikiMap {
    constructor(containerId, options = {}) {
        this.containerId = containerId;
        this.options = {
            center: [39.8283, -98.5795], // Center of USA
            zoom: 4,
            minZoom: 2,
            maxZoom: 18,
            enableClustering: true,
            enableDarkMode: true,
            enableGeolocation: false,
            apiEndpoints: {
                locations: '/api/map/locations/',
                details: '/api/map/location-detail/'
            },
            ...options
        };
        
        this.map = null;
        this.markers = null;
        this.currentData = [];
        this.userLocation = null;
        this.currentTileLayer = null;
        this.boundsUpdateTimeout = null;
        
        // Event handlers
        this.eventHandlers = {
            locationClick: [],
            boundsChange: [],
            dataLoad: []
        };
        
        this.init();
    }
    
    /**
     * Initialize the map
     */
    init() {
        const container = document.getElementById(this.containerId);
        if (!container) {
            console.error(`Map container with ID '${this.containerId}' not found`);
            return;
        }
        
        try {
            this.initializeMap();
            this.setupTileLayers();
            this.setupClustering();
            this.bindEvents();
            this.loadInitialData();
        } catch (error) {
            console.error('Failed to initialize map:', error);
        }
    }
    
    /**
     * Initialize the Leaflet map instance
     */
    initializeMap() {
        this.map = L.map(this.containerId, {
            center: this.options.center,
            zoom: this.options.zoom,
            minZoom: this.options.minZoom,
            maxZoom: this.options.maxZoom,
            zoomControl: false,
            attributionControl: false
        });
        
        // Add custom zoom control
        L.control.zoom({
            position: 'bottomright'
        }).addTo(this.map);
        
        // Add attribution control
        L.control.attribution({
            position: 'bottomleft',
            prefix: false
        }).addTo(this.map);
    }
    
    /**
     * Setup tile layers with dark mode support
     */
    setupTileLayers() {
        this.tileLayers = {
            light: L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: '© OpenStreetMap contributors',
                className: 'map-tiles-light'
            }),
            dark: L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
                attribution: '© OpenStreetMap contributors, © CARTO',
                className: 'map-tiles-dark'
            }),
            satellite: L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
                attribution: '© Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community',
                className: 'map-tiles-satellite'
            })
        };
        
        // Set initial tile layer based on theme
        this.updateTileLayer();
        
        // Listen for theme changes if dark mode is enabled
        if (this.options.enableDarkMode) {
            this.observeThemeChanges();
        }
    }
    
    /**
     * Setup marker clustering
     */
    setupClustering() {
        if (this.options.enableClustering) {
            this.markers = L.markerClusterGroup({
                chunkedLoading: true,
                maxClusterRadius: 50,
                spiderfyOnMaxZoom: true,
                showCoverageOnHover: false,
                zoomToBoundsOnClick: true,
                iconCreateFunction: (cluster) => {
                    const count = cluster.getChildCount();
                    let className = 'cluster-marker-small';
                    
                    if (count > 100) className = 'cluster-marker-large';
                    else if (count > 10) className = 'cluster-marker-medium';
                    
                    return L.divIcon({
                        html: `<div class="cluster-marker-inner">${count}</div>`,
                        className: `cluster-marker ${className}`,
                        iconSize: L.point(40, 40)
                    });
                }
            });
        } else {
            this.markers = L.layerGroup();
        }
        
        this.map.addLayer(this.markers);
    }
    
    /**
     * Bind map events
     */
    bindEvents() {
        // Map movement events
        this.map.on('moveend zoomend', () => {
            this.handleBoundsChange();
        });
        
        // Marker click events
        this.markers.on('click', (e) => {
            if (e.layer.options && e.layer.options.locationData) {
                this.handleLocationClick(e.layer.options.locationData);
            }
        });
        
        // Custom event handlers
        this.map.on('locationfound', (e) => {
            this.handleLocationFound(e);
        });
        
        this.map.on('locationerror', (e) => {
            this.handleLocationError(e);
        });
    }
    
    /**
     * Observe theme changes for automatic tile layer switching
     */
    observeThemeChanges() {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.attributeName === 'class') {
                    this.updateTileLayer();
                }
            });
        });
        
        observer.observe(document.documentElement, {
            attributes: true,
            attributeFilter: ['class']
        });
    }
    
    /**
     * Update tile layer based on current theme and settings
     */
    updateTileLayer() {
        // Remove current tile layer
        if (this.currentTileLayer) {
            this.map.removeLayer(this.currentTileLayer);
        }
        
        // Determine which layer to use
        let layerType = 'light';
        
        if (document.documentElement.classList.contains('dark')) {
            layerType = 'dark';
        }
        
        // Check for satellite mode toggle
        const satelliteToggle = document.querySelector('input[name="satellite"]');
        if (satelliteToggle && satelliteToggle.checked) {
            layerType = 'satellite';
        }
        
        // Add the appropriate tile layer
        this.currentTileLayer = this.tileLayers[layerType];
        this.map.addLayer(this.currentTileLayer);
    }
    
    /**
     * Load initial map data
     */
    async loadInitialData() {
        const bounds = this.map.getBounds();
        await this.loadLocations(bounds, {});
    }
    
    /**
     * Load locations with optional bounds and filters
     */
    async loadLocations(bounds = null, filters = {}) {
        try {
            this.showLoading(true);
            
            const params = new URLSearchParams();
            
            // Add bounds if provided
            if (bounds) {
                params.append('north', bounds.getNorth());
                params.append('south', bounds.getSouth());
                params.append('east', bounds.getEast());
                params.append('west', bounds.getWest());
            }
            
            // Add zoom level
            params.append('zoom', this.map.getZoom());
            
            // Add filters
            Object.entries(filters).forEach(([key, value]) => {
                if (Array.isArray(value)) {
                    value.forEach(v => params.append(key, v));
                } else if (value !== null && value !== undefined && value !== '') {
                    params.append(key, value);
                }
            });
            
            const response = await fetch(`${this.options.apiEndpoints.locations}?${params}`);
            const data = await response.json();
            
            if (data.status === 'success') {
                this.updateMarkers(data.data);
                this.triggerEvent('dataLoad', data.data);
            } else {
                console.error('Map data error:', data.message);
            }
        } catch (error) {
            console.error('Failed to load map data:', error);
        } finally {
            this.showLoading(false);
        }
    }
    
    /**
     * Update map markers with new data
     */
    updateMarkers(data) {
        // Clear existing markers
        this.markers.clearLayers();
        this.currentData = data;
        
        // Add location markers
        if (data.locations) {
            data.locations.forEach(location => {
                this.addLocationMarker(location);
            });
        }
        
        // Add cluster markers (if not using Leaflet clustering)
        if (data.clusters && !this.options.enableClustering) {
            data.clusters.forEach(cluster => {
                this.addClusterMarker(cluster);
            });
        }
    }
    
    /**
     * Add a location marker to the map
     */
    addLocationMarker(location) {
        const icon = this.createLocationIcon(location);
        const marker = L.marker([location.latitude, location.longitude], {
            icon: icon,
            locationData: location
        });
        
        // Create popup content
        const popupContent = this.createPopupContent(location);
        marker.bindPopup(popupContent, {
            maxWidth: 300,
            className: 'location-popup'
        });
        
        this.markers.addLayer(marker);
    }
    
    /**
     * Add a cluster marker (for server-side clustering)
     */
    addClusterMarker(cluster) {
        const marker = L.marker([cluster.latitude, cluster.longitude], {
            icon: L.divIcon({
                className: 'cluster-marker server-cluster',
                html: `<div class="cluster-marker-inner">${cluster.count}</div>`,
                iconSize: [40, 40]
            })
        });
        
        marker.bindPopup(`${cluster.count} locations in this area`);
        this.markers.addLayer(marker);
    }
    
    /**
     * Create location icon based on type
     */
    createLocationIcon(location) {
        const iconMap = {
            'park': { emoji: '🎢', color: '#10B981' },
            'ride': { emoji: '🎠', color: '#3B82F6' },
            'company': { emoji: '🏢', color: '#8B5CF6' },
            'generic': { emoji: '📍', color: '#6B7280' }
        };
        
        const iconData = iconMap[location.type] || iconMap.generic;
        
        return L.divIcon({
            className: 'location-marker',
            html: `
                <div class="location-marker-inner" style="background-color: ${iconData.color}">
                    <span class="location-marker-emoji">${iconData.emoji}</span>
                </div>
            `,
            iconSize: [30, 30],
            iconAnchor: [15, 15],
            popupAnchor: [0, -15]
        });
    }
    
    /**
     * Create popup content for a location
     */
    createPopupContent(location) {
        return `
            <div class="location-info-popup">
                <h3 class="popup-title">${location.name}</h3>
                ${location.formatted_location ? `<p class="popup-location"><i class="fas fa-map-marker-alt"></i>${location.formatted_location}</p>` : ''}
                ${location.operator ? `<p class="popup-operator"><i class="fas fa-building"></i>${location.operator}</p>` : ''}
                ${location.ride_count ? `<p class="popup-rides"><i class="fas fa-rocket"></i>${location.ride_count} rides</p>` : ''}
                ${location.status ? `<p class="popup-status"><i class="fas fa-info-circle"></i>${location.status}</p>` : ''}
                <div class="popup-actions">
                    <button onclick="window.thrillwikiMap.showLocationDetails('${location.type}', ${location.id})" 
                            class="btn btn-primary btn-sm">
                        <i class="fas fa-eye"></i> View Details
                    </button>
                </div>
            </div>
        `;
    }
    
    /**
     * Show/hide loading indicator
     */
    showLoading(show) {
        const loadingElement = document.getElementById(`${this.containerId}-loading`) || 
                              document.getElementById('map-loading');
        
        if (loadingElement) {
            loadingElement.style.display = show ? 'flex' : 'none';
        }
    }
    
    /**
     * Handle map bounds change
     */
    handleBoundsChange() {
        clearTimeout(this.boundsUpdateTimeout);
        this.boundsUpdateTimeout = setTimeout(() => {
            const bounds = this.map.getBounds();
            this.triggerEvent('boundsChange', bounds);
            
            // Auto-reload data on significant bounds change
            if (this.shouldReloadData()) {
                this.loadLocations(bounds, this.getCurrentFilters());
            }
        }, 1000);
    }
    
    /**
     * Handle location click
     */
    handleLocationClick(location) {
        this.triggerEvent('locationClick', location);
    }
    
    /**
     * Show location details (integrate with HTMX)
     */
    showLocationDetails(type, id) {
        const url = `${this.options.apiEndpoints.details}${type}/${id}/`;
        
        if (typeof htmx !== 'undefined') {
            htmx.ajax('GET', url, {
                target: '#location-modal',
                swap: 'innerHTML'
            }).then(() => {
                const modal = document.getElementById('location-modal');
                if (modal) {
                    modal.classList.remove('hidden');
                }
            });
        } else {
            // Fallback to regular navigation
            window.location.href = url;
        }
    }
    
    /**
     * Get current filters from form
     */
    getCurrentFilters() {
        const form = document.getElementById('map-filters');
        if (!form) return {};
        
        const formData = new FormData(form);
        const filters = {};
        
        for (let [key, value] of formData.entries()) {
            if (filters[key]) {
                if (Array.isArray(filters[key])) {
                    filters[key].push(value);
                } else {
                    filters[key] = [filters[key], value];
                }
            } else {
                filters[key] = value;
            }
        }
        
        return filters;
    }
    
    /**
     * Update filters and reload data
     */
    updateFilters(filters) {
        const bounds = this.map.getBounds();
        this.loadLocations(bounds, filters);
    }
    
    /**
     * Enable user location features
     */
    enableGeolocation() {
        this.options.enableGeolocation = true;
        this.map.locate({ setView: false, maxZoom: 16 });
    }
    
    /**
     * Handle location found
     */
    handleLocationFound(e) {
        if (this.userLocation) {
            this.map.removeLayer(this.userLocation);
        }
        
        this.userLocation = L.marker(e.latlng, {
            icon: 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]
            })
        }).addTo(this.map);
        
        this.userLocation.bindPopup('Your Location');
    }
    
    /**
     * Handle location error
     */
    handleLocationError(e) {
        console.warn('Location access denied or unavailable:', e.message);
    }
    
    /**
     * Determine if data should be reloaded based on map movement
     */
    shouldReloadData() {
        // Simple heuristic: reload if zoom changed or moved significantly
        return true; // For now, always reload
    }
    
    /**
     * 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);
                }
            });
        }
    }
    
    /**
     * Export map view as image (requires html2canvas)
     */
    async exportMap() {
        if (typeof html2canvas === 'undefined') {
            console.warn('html2canvas library not loaded, cannot export map');
            return null;
        }
        
        try {
            const canvas = await html2canvas(document.getElementById(this.containerId));
            return canvas.toDataURL('image/png');
        } catch (error) {
            console.error('Failed to export map:', error);
            return null;
        }
    }
    
    /**
     * Resize map (call when container size changes)
     */
    invalidateSize() {
        if (this.map) {
            this.map.invalidateSize();
        }
    }
    
    /**
     * Get map bounds
     */
    getBounds() {
        return this.map ? this.map.getBounds() : null;
    }
    
    /**
     * Set map view
     */
    setView(latlng, zoom) {
        if (this.map) {
            this.map.setView(latlng, zoom);
        }
    }
    
    /**
     * Fit map to bounds
     */
    fitBounds(bounds, options = {}) {
        if (this.map) {
            this.map.fitBounds(bounds, options);
        }
    }
    
    /**
     * Destroy map instance
     */
    destroy() {
        if (this.map) {
            this.map.remove();
            this.map = null;
        }
        
        // Clear timeouts
        if (this.boundsUpdateTimeout) {
            clearTimeout(this.boundsUpdateTimeout);
        }
        
        // Clear event handlers
        this.eventHandlers = {};
    }
}

// Auto-initialize maps with data attributes
document.addEventListener('DOMContentLoaded', function() {
    // Find all elements with map-container class
    const mapContainers = document.querySelectorAll('[data-map="auto"]');
    
    mapContainers.forEach(container => {
        const mapId = container.id;
        const options = {};
        
        // Parse data attributes for configuration
        Object.keys(container.dataset).forEach(key => {
            if (key.startsWith('map')) {
                const optionKey = key.replace('map', '').toLowerCase();
                let value = container.dataset[key];
                
                // Try to parse as JSON for complex values
                try {
                    value = JSON.parse(value);
                } catch (e) {
                    // Keep as string if not valid JSON
                }
                
                options[optionKey] = value;
            }
        });
        
        // Create map instance
        window[`${mapId}Instance`] = new ThrillWikiMap(mapId, options);
    });
});

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