/**
 * ThrillWiki HTMX Maps Integration - Dynamic Map Updates via HTMX
 * 
 * This module handles HTMX events for map updates, manages loading states
 * during API calls, updates map content based on HTMX responses, and provides
 * error handling for failed requests
 */

class HTMXMapIntegration {
    constructor(options = {}) {
        this.options = {
            mapInstance: null,
            filterInstance: null,
            defaultTarget: '#map-container',
            loadingClass: 'htmx-loading',
            errorClass: 'htmx-error',
            successClass: 'htmx-success',
            loadingTimeout: 30000, // 30 seconds
            retryAttempts: 3,
            retryDelay: 1000,
            ...options
        };
        
        this.loadingElements = new Set();
        this.activeRequests = new Map();
        this.requestQueue = [];
        this.retryCount = new Map();
        
        this.init();
    }
    
    /**
     * Initialize HTMX integration
     */
    init() {
        if (typeof htmx === 'undefined') {
            console.warn('HTMX not found, map integration disabled');
            return;
        }
        
        this.setupEventHandlers();
        this.setupCustomEvents();
        this.setupErrorHandling();
        this.enhanceExistingElements();
    }
    
    /**
     * Setup HTMX event handlers
     */
    setupEventHandlers() {
        // Before request - show loading states
        document.addEventListener('htmx:beforeRequest', (e) => {
            this.handleBeforeRequest(e);
        });
        
        // After request - handle response and update maps
        document.addEventListener('htmx:afterRequest', (e) => {
            this.handleAfterRequest(e);
        });
        
        // Response error - handle failed requests
        document.addEventListener('htmx:responseError', (e) => {
            this.handleResponseError(e);
        });
        
        // Send error - handle network errors
        document.addEventListener('htmx:sendError', (e) => {
            this.handleSendError(e);
        });
        
        // Timeout - handle request timeouts
        document.addEventListener('htmx:timeout', (e) => {
            this.handleTimeout(e);
        });
        
        // Before swap - prepare for content updates
        document.addEventListener('htmx:beforeSwap', (e) => {
            this.handleBeforeSwap(e);
        });
        
        // After swap - update maps with new content
        document.addEventListener('htmx:afterSwap', (e) => {
            this.handleAfterSwap(e);
        });
        
        // Config request - modify requests before sending
        document.addEventListener('htmx:configRequest', (e) => {
            this.handleConfigRequest(e);
        });
    }
    
    /**
     * Setup custom map-specific events
     */
    setupCustomEvents() {
        // Custom event for map data updates
        document.addEventListener('map:dataUpdate', (e) => {
            this.handleMapDataUpdate(e);
        });
        
        // Custom event for filter changes
        document.addEventListener('filter:changed', (e) => {
            this.handleFilterChange(e);
        });
        
        // Custom event for search updates
        document.addEventListener('search:results', (e) => {
            this.handleSearchResults(e);
        });
    }
    
    /**
     * Setup global error handling
     */
    setupErrorHandling() {
        // Global error handler
        window.addEventListener('error', (e) => {
            if (e.filename && e.filename.includes('htmx')) {
                console.error('HTMX error:', e.error);
                this.showErrorMessage('An error occurred while updating the map');
            }
        });
        
        // Unhandled promise rejection handler
        window.addEventListener('unhandledrejection', (e) => {
            if (e.reason && e.reason.toString().includes('htmx')) {
                console.error('HTMX promise rejection:', e.reason);
                this.showErrorMessage('Failed to complete map request');
            }
        });
    }
    
    /**
     * Enhance existing elements with HTMX map functionality
     */
    enhanceExistingElements() {
        // Add map-specific attributes to filter forms
        const filterForms = document.querySelectorAll('[data-map-filter]');
        filterForms.forEach(form => {
            if (!form.hasAttribute('hx-get')) {
                form.setAttribute('hx-get', form.getAttribute('data-map-filter'));
                form.setAttribute('hx-trigger', 'change, submit');
                form.setAttribute('hx-target', '#map-container');
                form.setAttribute('hx-swap', 'none');
            }
        });
        
        // Add map update attributes to search inputs
        const searchInputs = document.querySelectorAll('[data-map-search]');
        searchInputs.forEach(input => {
            if (!input.hasAttribute('hx-get')) {
                input.setAttribute('hx-get', input.getAttribute('data-map-search'));
                input.setAttribute('hx-trigger', 'input changed delay:500ms');
                input.setAttribute('hx-target', '#search-results');
            }
        });
    }
    
    /**
     * Handle before request event
     */
    handleBeforeRequest(e) {
        const element = e.target;
        const requestId = this.generateRequestId();
        
        // Store request information
        this.activeRequests.set(requestId, {
            element: element,
            startTime: Date.now(),
            url: e.detail.requestConfig.path
        });
        
        // Show loading state
        this.showLoadingState(element, true);
        
        // Add request ID to detail for tracking
        e.detail.requestId = requestId;
        
        // Set timeout
        setTimeout(() => {
            if (this.activeRequests.has(requestId)) {
                this.handleTimeout({ detail: { requestId } });
            }
        }, this.options.loadingTimeout);
        
        console.log('HTMX request started:', e.detail.requestConfig.path);
    }
    
    /**
     * Handle after request event
     */
    handleAfterRequest(e) {
        const requestId = e.detail.requestId;
        const request = this.activeRequests.get(requestId);
        
        if (request) {
            const duration = Date.now() - request.startTime;
            console.log(`HTMX request completed in ${duration}ms:`, request.url);
            
            this.activeRequests.delete(requestId);
            this.showLoadingState(request.element, false);
        }
        
        if (e.detail.successful) {
            this.handleSuccessfulResponse(e);
        } else {
            this.handleFailedResponse(e);
        }
    }
    
    /**
     * Handle successful response
     */
    handleSuccessfulResponse(e) {
        const element = e.target;
        
        // Add success class temporarily
        element.classList.add(this.options.successClass);
        setTimeout(() => {
            element.classList.remove(this.options.successClass);
        }, 2000);
        
        // Reset retry count
        this.retryCount.delete(element);
        
        // Check if this is a map-related request
        if (this.isMapRequest(e)) {
            this.updateMapFromResponse(e);
        }
    }
    
    /**
     * Handle failed response
     */
    handleFailedResponse(e) {
        const element = e.target;
        
        // Add error class
        element.classList.add(this.options.errorClass);
        setTimeout(() => {
            element.classList.remove(this.options.errorClass);
        }, 5000);
        
        // Check if we should retry
        if (this.shouldRetry(element)) {
            this.scheduleRetry(element, e.detail);
        } else {
            this.showErrorMessage('Failed to update map data');
        }
    }
    
    /**
     * Handle response error
     */
    handleResponseError(e) {
        console.error('HTMX response error:', e.detail);
        
        const element = e.target;
        const status = e.detail.xhr.status;
        
        let message = 'An error occurred while updating the map';
        
        switch (status) {
            case 400:
                message = 'Invalid request parameters';
                break;
            case 401:
                message = 'Authentication required';
                break;
            case 403:
                message = 'Access denied';
                break;
            case 404:
                message = 'Map data not found';
                break;
            case 429:
                message = 'Too many requests. Please wait a moment.';
                break;
            case 500:
                message = 'Server error. Please try again later.';
                break;
        }
        
        this.showErrorMessage(message);
        this.showLoadingState(element, false);
    }
    
    /**
     * Handle send error
     */
    handleSendError(e) {
        console.error('HTMX send error:', e.detail);
        this.showErrorMessage('Network error. Please check your connection.');
        this.showLoadingState(e.target, false);
    }
    
    /**
     * Handle timeout
     */
    handleTimeout(e) {
        console.warn('HTMX request timeout');
        
        if (e.detail.requestId) {
            const request = this.activeRequests.get(e.detail.requestId);
            if (request) {
                this.showLoadingState(request.element, false);
                this.activeRequests.delete(e.detail.requestId);
            }
        }
        
        this.showErrorMessage('Request timed out. Please try again.');
    }
    
    /**
     * Handle before swap
     */
    handleBeforeSwap(e) {
        // Prepare map for content update
        if (this.isMapRequest(e)) {
            console.log('Preparing map for content swap');
        }
    }
    
    /**
     * Handle after swap
     */
    handleAfterSwap(e) {
        // Re-initialize any new HTMX elements
        this.enhanceExistingElements();
        
        // Update maps if needed
        if (this.isMapRequest(e)) {
            this.reinitializeMapComponents();
        }
    }
    
    /**
     * Handle config request
     */
    handleConfigRequest(e) {
        const config = e.detail;
        
        // Add CSRF token if available
        const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]');
        if (csrfToken && (config.verb === 'post' || config.verb === 'put' || config.verb === 'patch')) {
            config.headers['X-CSRFToken'] = csrfToken.value;
        }
        
        // Add map-specific headers
        if (this.isMapRequest(e)) {
            config.headers['X-Map-Request'] = 'true';
            
            // Add current map bounds if available
            if (this.options.mapInstance) {
                const bounds = this.options.mapInstance.getBounds();
                if (bounds) {
                    config.headers['X-Map-Bounds'] = JSON.stringify({
                        north: bounds.getNorth(),
                        south: bounds.getSouth(),
                        east: bounds.getEast(),
                        west: bounds.getWest(),
                        zoom: this.options.mapInstance.getZoom()
                    });
                }
            }
        }
    }
    
    /**
     * Handle map data updates
     */
    handleMapDataUpdate(e) {
        if (this.options.mapInstance) {
            const data = e.detail;
            this.options.mapInstance.updateMarkers(data);
        }
    }
    
    /**
     * Handle filter changes
     */
    handleFilterChange(e) {
        if (this.options.filterInstance) {
            const filters = e.detail;
            
            // Trigger HTMX request for filter update
            const filterForm = document.getElementById('map-filters');
            if (filterForm && filterForm.hasAttribute('hx-get')) {
                htmx.trigger(filterForm, 'change');
            }
        }
    }
    
    /**
     * Handle search results
     */
    handleSearchResults(e) {
        const results = e.detail;
        
        // Update map with search results if applicable
        if (results.locations && this.options.mapInstance) {
            this.options.mapInstance.updateMarkers({ locations: results.locations });
        }
    }
    
    /**
     * Show/hide loading state
     */
    showLoadingState(element, show) {
        if (show) {
            element.classList.add(this.options.loadingClass);
            this.loadingElements.add(element);
            
            // Show loading indicators
            const indicators = element.querySelectorAll('.htmx-indicator');
            indicators.forEach(indicator => {
                indicator.style.display = 'block';
            });
            
            // Disable form elements
            const inputs = element.querySelectorAll('input, button, select');
            inputs.forEach(input => {
                input.disabled = true;
            });
        } else {
            element.classList.remove(this.options.loadingClass);
            this.loadingElements.delete(element);
            
            // Hide loading indicators
            const indicators = element.querySelectorAll('.htmx-indicator');
            indicators.forEach(indicator => {
                indicator.style.display = 'none';
            });
            
            // Re-enable form elements
            const inputs = element.querySelectorAll('input, button, select');
            inputs.forEach(input => {
                input.disabled = false;
            });
        }
    }
    
    /**
     * Check if request is map-related
     */
    isMapRequest(e) {
        const element = e.target;
        const url = e.detail.requestConfig ? e.detail.requestConfig.path : '';
        
        return element.hasAttribute('data-map-filter') ||
               element.hasAttribute('data-map-search') ||
               element.closest('[data-map-target]') ||
               url.includes('/api/map/') ||
               url.includes('/maps/');
    }
    
    /**
     * Update map from HTMX response
     */
    updateMapFromResponse(e) {
        if (!this.options.mapInstance) return;
        
        try {
            // Try to extract map data from response
            const responseText = e.detail.xhr.responseText;
            
            // If response is JSON, update map directly
            try {
                const data = JSON.parse(responseText);
                if (data.status === 'success' && data.data) {
                    this.options.mapInstance.updateMarkers(data.data);
                }
            } catch (jsonError) {
                // If not JSON, look for data attributes in HTML
                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = responseText;
                
                const mapData = tempDiv.querySelector('[data-map-data]');
                if (mapData) {
                    const data = JSON.parse(mapData.getAttribute('data-map-data'));
                    this.options.mapInstance.updateMarkers(data);
                }
            }
        } catch (error) {
            console.error('Failed to update map from response:', error);
        }
    }
    
    /**
     * Check if element should be retried
     */
    shouldRetry(element) {
        const retryCount = this.retryCount.get(element) || 0;
        return retryCount < this.options.retryAttempts;
    }
    
    /**
     * Schedule retry for failed request
     */
    scheduleRetry(element, detail) {
        const retryCount = (this.retryCount.get(element) || 0) + 1;
        this.retryCount.set(element, retryCount);
        
        const delay = this.options.retryDelay * Math.pow(2, retryCount - 1); // Exponential backoff
        
        setTimeout(() => {
            console.log(`Retrying HTMX request (attempt ${retryCount})`);
            htmx.trigger(element, 'retry');
        }, delay);
    }
    
    /**
     * Show error message to user
     */
    showErrorMessage(message) {
        // Create or update error message element
        let errorEl = document.getElementById('htmx-error-message');
        
        if (!errorEl) {
            errorEl = document.createElement('div');
            errorEl.id = 'htmx-error-message';
            errorEl.className = 'htmx-error-message';
            
            // Insert at top of page
            document.body.insertBefore(errorEl, document.body.firstChild);
        }
        
        errorEl.innerHTML = `
            <div class="error-content">
                <i class="fas fa-exclamation-circle"></i>
                <span>${message}</span>
                <button onclick="this.parentElement.parentElement.remove()" class="error-close">
                    <i class="fas fa-times"></i>
                </button>
            </div>
        `;
        
        errorEl.style.display = 'block';
        
        // Auto-hide after 10 seconds
        setTimeout(() => {
            if (errorEl.parentNode) {
                errorEl.remove();
            }
        }, 10000);
    }
    
    /**
     * Reinitialize map components after content swap
     */
    reinitializeMapComponents() {
        // Reinitialize filter components
        if (this.options.filterInstance) {
            this.options.filterInstance.init();
        }
        
        // Reinitialize any new map containers
        const newMapContainers = document.querySelectorAll('[data-map="auto"]:not([data-initialized])');
        newMapContainers.forEach(container => {
            container.setAttribute('data-initialized', 'true');
            // Initialize new map instance if needed
        });
    }
    
    /**
     * Generate unique request ID
     */
    generateRequestId() {
        return `htmx-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
    
    /**
     * Connect to map instance
     */
    connectToMap(mapInstance) {
        this.options.mapInstance = mapInstance;
    }
    
    /**
     * Connect to filter instance
     */
    connectToFilter(filterInstance) {
        this.options.filterInstance = filterInstance;
    }
    
    /**
     * Get active request count
     */
    getActiveRequestCount() {
        return this.activeRequests.size;
    }
    
    /**
     * Cancel all active requests
     */
    cancelAllRequests() {
        this.activeRequests.forEach((request, id) => {
            this.showLoadingState(request.element, false);
        });
        this.activeRequests.clear();
    }
    
    /**
     * Get loading elements
     */
    getLoadingElements() {
        return Array.from(this.loadingElements);
    }
}

// Auto-initialize HTMX integration
document.addEventListener('DOMContentLoaded', function() {
    window.htmxMapIntegration = new HTMXMapIntegration();
    
    // Connect to existing instances
    if (window.thrillwikiMap) {
        window.htmxMapIntegration.connectToMap(window.thrillwikiMap);
    }
    
    if (window.mapFilters) {
        window.htmxMapIntegration.connectToFilter(window.mapFilters);
    }
});

// Add styles for HTMX integration
document.addEventListener('DOMContentLoaded', function() {
    if (document.getElementById('htmx-map-styles')) return;
    
    const styles = `
        <style id="htmx-map-styles">
            .htmx-loading {
                opacity: 0.7;
                pointer-events: none;
            }
            
            .htmx-error {
                border-color: #EF4444;
                background-color: #FEE2E2;
            }
            
            .htmx-success {
                border-color: #10B981;
                background-color: #D1FAE5;
            }
            
            .htmx-indicator {
                display: none;
            }
            
            .htmx-error-message {
                position: fixed;
                top: 20px;
                right: 20px;
                z-index: 10000;
                max-width: 400px;
                background: #FEE2E2;
                border: 1px solid #FCA5A5;
                border-radius: 8px;
                padding: 0;
                box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
                animation: slideInRight 0.3s ease;
            }
            
            .error-content {
                display: flex;
                align-items: center;
                gap: 8px;
                padding: 12px 16px;
                color: #991B1B;
                font-size: 14px;
            }
            
            .error-close {
                background: none;
                border: none;
                color: #991B1B;
                cursor: pointer;
                padding: 4px;
                margin-left: auto;
            }
            
            .error-close:hover {
                color: #7F1D1D;
            }
            
            @keyframes slideInRight {
                from {
                    transform: translateX(100%);
                    opacity: 0;
                }
                to {
                    transform: translateX(0);
                    opacity: 1;
                }
            }
            
            /* Dark mode styles */
            .dark .htmx-error-message {
                background: #7F1D1D;
                border-color: #991B1B;
            }
            
            .dark .error-content {
                color: #FCA5A5;
            }
            
            .dark .error-close {
                color: #FCA5A5;
            }
            
            .dark .error-close:hover {
                color: #F87171;
            }
        </style>
    `;
    
    document.head.insertAdjacentHTML('beforeend', styles);
});

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