/**
 * Enhanced Error Handling System
 * User-friendly messages, retry buttons, offline queue, graceful degradation, and recovery options
 */

class EnhancedErrorHandler {
    constructor() {
        this.retryQueue = [];
        this.offlineQueue = [];
        this.isOnline = navigator.onLine;
        this.retryAttempts = new Map();
        this.maxRetries = 3;
        // Track recent errors to prevent duplicates
        this.recentErrors = new Map(); // message -> timestamp
        this.errorDeduplicationWindow = 60000; // 1 minute - don't show same error twice within this window
        this.init();
    }

    init() {
        // Setup feature fallbacks first (before other initialization)
        this.setupFeatureFallbacks();
        
        // Setup network status monitoring
        this.setupNetworkMonitoring();
        
        // Setup fetch interceptors
        this.setupFetchInterceptor();
        
        // Setup global error handlers
        this.setupGlobalErrorHandlers();
        
        // Setup offline queue processor
        this.setupOfflineQueue();
        
        // Setup graceful degradation
        this.setupGracefulDegradation();
    }

    /**
     * Network Status Monitoring
     */
    setupNetworkMonitoring() {
        window.addEventListener('online', () => {
            this.isOnline = true;
            this.handleOnline();
        });

        window.addEventListener('offline', () => {
            this.isOnline = false;
            this.handleOffline();
        });

        // Periodic online check - every 30 seconds
        // This helps detect cases where browser thinks it's online but server is unreachable
        // The browser's online/offline events handle most cases, this is a fallback
        setInterval(() => {
            this.checkOnlineStatus();
        }, 30000); // Check every 30 seconds instead of 5
    }

    checkOnlineStatus() {
        fetch('/api/health', { method: 'GET', cache: 'no-cache' })
            .then((response) => {
                if (response.ok && !this.isOnline) {
                    this.isOnline = true;
                    this.handleOnline();
                }
            })
            .catch(() => {
                if (this.isOnline) {
                    this.isOnline = false;
                    this.handleOffline();
                }
            });
    }

    handleOnline() {
        // Show online indicator
        this.showOnlineIndicator();
        
        // Process offline queue
        this.processOfflineQueue();
        
        // Retry failed operations
        this.retryFailedOperations();
    }

    handleOffline() {
        // Show offline indicator
        this.showOfflineIndicator();
    }

    showOnlineIndicator() {
        if (window.toastManager) {
            window.toastManager.success(
                'Connection restored. Processing queued operations...',
                'Back Online',
                3000
            );
        }
    }

    showOfflineIndicator() {
        // Create persistent offline indicator
        if (document.getElementById('offline-indicator')) return;

        const indicator = document.createElement('div');
        indicator.id = 'offline-indicator';
        indicator.className = 'offline-indicator';
        indicator.innerHTML = `
            <div class="offline-indicator-content">
                <i class="fas fa-wifi-slash"></i>
                <span>You're offline. Some features may be limited.</span>
                <span class="offline-queue-count" id="offline-queue-count"></span>
            </div>
        `;
        document.body.appendChild(indicator);

        // Add styles
        this.addOfflineIndicatorStyles();
    }

    addOfflineIndicatorStyles() {
        if (document.getElementById('offline-indicator-styles')) return;

        const style = document.createElement('style');
        style.id = 'offline-indicator-styles';
        style.textContent = `
            .offline-indicator {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                background: #f59e0b;
                color: white;
                padding: 12px 16px;
                text-align: center;
                z-index: 9999;
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
                animation: slideDown 0.3s ease-out;
            }
            
            .dark .offline-indicator {
                background: #d97706;
            }
            
            .offline-indicator-content {
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 8px;
                font-size: 0.875rem;
            }
            
            .offline-queue-count {
                margin-left: 8px;
                font-weight: 600;
            }
            
            @keyframes slideDown {
                from {
                    transform: translateY(-100%);
                }
                to {
                    transform: translateY(0);
                }
            }
            
            .error-retry-container {
                margin-top: 12px;
                padding-top: 12px;
                border-top: 1px solid rgba(255, 255, 255, 0.2);
                display: flex;
                flex-direction: column;
                gap: 8px;
            }
            
            .error-retry-btn {
                padding: 8px 16px;
                background: rgba(255, 255, 255, 0.2);
                color: white;
                border: 1px solid rgba(255, 255, 255, 0.3);
                border-radius: 6px;
                cursor: pointer;
                font-size: 0.875rem;
                font-weight: 500;
                transition: all 0.2s;
                align-self: flex-start;
            }
            
            .error-retry-btn:hover {
                background: rgba(255, 255, 255, 0.3);
            }
            
            .error-retry-btn:disabled {
                opacity: 0.5;
                cursor: not-allowed;
            }
            
            .error-message-friendly {
                margin-bottom: 8px;
            }
            
            .error-recovery-options {
                display: flex;
                flex-wrap: wrap;
                gap: 8px;
                margin-top: 4px;
            }
            
            .error-recovery-btn {
                padding: 6px 12px;
                background: rgba(255, 255, 255, 0.15);
                color: white;
                border: 1px solid rgba(255, 255, 255, 0.25);
                border-radius: 6px;
                cursor: pointer;
                font-size: 0.875rem;
                transition: all 0.2s;
            }
            
            .error-recovery-btn:hover {
                background: rgba(255, 255, 255, 0.25);
            }
        `;
        document.head.appendChild(style);
    }

    /**
     * Fetch Interceptor for Error Handling
     */
    setupFetchInterceptor() {
        const originalFetch = window.fetch;
        
        window.fetch = async (...args) => {
            const [url, options = {}] = args;
            
            try {
                const response = await originalFetch(...args);
                
                // Handle non-ok responses
                if (!response.ok) {
                    return this.handleFetchError(response, url, options);
                }
                
                return response;
            } catch (error) {
                // Network error or other fetch error
                return this.handleFetchException(error, url, options);
            }
        };
    }

    async handleFetchError(response, url, options) {
        // Clone the response before reading it, so the caller can still read it
        const clonedResponse = response.clone();
        const errorData = await clonedResponse.json().catch(() => ({ error: 'Unknown error' }));
        const userFriendlyMessage = this.getUserFriendlyMessage(response.status, errorData);
        
        // Show error notification with retry option
        const errorId = this.showErrorWithRetry(userFriendlyMessage, response.status, () => {
            return this.retryFetch(url, options);
        });
        
        // Queue for offline processing if offline
        if (!this.isOnline) {
            await this.queueForOffline(url, options, errorId);
        }

        // Return original response so caller can handle it
        return response;
    }

    async handleFetchException(error, url, options) {
        // Network error
        if (!this.isOnline) {
            await this.queueForOffline(url, options);
            return new Response(JSON.stringify({ error: 'Offline' }), {
                status: 0,
                statusText: 'Offline'
            });
        }
        
        const userFriendlyMessage = this.getUserFriendlyMessage(0, error);
        const errorId = this.showErrorWithRetry(userFriendlyMessage, 0, () => {
            return this.retryFetch(url, options);
        });
        
        return new Response(JSON.stringify({ error: userFriendlyMessage }), {
            status: 0,
            statusText: 'Network Error'
        });
    }

    async retryFetch(url, options) {
        const retryKey = `${url}-${JSON.stringify(options)}`;
        const attempts = this.retryAttempts.get(retryKey) || 0;
        
        if (attempts >= this.maxRetries) {
            this.showError(
                'Maximum retry attempts reached. Please try again later or contact support.',
                'Retry Failed'
            );
            return null;
        }
        
        this.retryAttempts.set(retryKey, attempts + 1);
        
        try {
            const response = await fetch(url, options);
            if (response.ok) {
                this.retryAttempts.delete(retryKey);
                return response;
            }
            throw new Error(`HTTP ${response.status}`);
        } catch (error) {
            if (attempts < this.maxRetries - 1) {
                // Wait before retrying (exponential backoff)
                await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempts) * 1000));
                return this.retryFetch(url, options);
            }
            throw error;
        }
    }

    /**
     * User-Friendly Error Messages
     */
    getUserFriendlyMessage(status, errorData) {
        const errorMessage = errorData?.error || errorData?.message || 'An error occurred';
        
        const messages = {
            0: 'Unable to connect to the server. Please check your internet connection.',
            400: 'Invalid request. Please check your input and try again.',
            401: 'You need to log in to access this feature.',
            403: 'You don\'t have permission to perform this action.',
            404: 'The requested resource was not found.',
            409: 'This action conflicts with existing data. Please refresh and try again.',
            422: 'Validation error. Please check your input.',
            429: 'Too many requests. Please wait a moment and try again.',
            500: 'A server error occurred. Our team has been notified.',
            502: 'The server is temporarily unavailable. Please try again later.',
            503: 'Service temporarily unavailable. Please try again in a few moments.',
            504: 'Request timeout. Please try again.'
        };
        
        // Try to get specific message from server
        if (typeof errorData === 'object' && errorData.message) {
            return errorData.message;
        }
        
        // Fallback to status-based message
        return messages[status] || `An error occurred (${status}). ${errorMessage}`;
    }

    /**
     * Show Error with Retry Button
     */
    showErrorWithRetry(message, status, retryCallback) {
        const recoveryOptions = this.getRecoveryOptions(status);
        
        // Create error toast with retry
        if (window.toastManager) {
            const toastId = window.toastManager.error(message, 'Error', 0);
            
            // Find toast element by ID
            const toastElement = window.toastManager.container.querySelector(
                `[data-toast-id="${toastId}"]`
            ) || Array.from(window.toastManager.container.children).find(
                el => el.getAttribute('data-toast-id') === String(toastId)
            );
            
            if (toastElement) {
                const retryContainer = document.createElement('div');
                retryContainer.className = 'error-retry-container';
                
                const retryBtn = document.createElement('button');
                retryBtn.className = 'error-retry-btn';
                retryBtn.type = 'button';
                retryBtn.setAttribute('aria-label', 'Retry');
                retryBtn.textContent = 'Retry';
                retryBtn.onclick = async () => {
                    retryBtn.disabled = true;
                    retryBtn.textContent = 'Retrying...';
                    
                    try {
                        await retryCallback();
                        window.toastManager.dismiss(toastId);
                        window.toastManager.success('Operation completed successfully', 'Success');
                    } catch (error) {
                        retryBtn.disabled = false;
                        retryBtn.textContent = 'Retry';
                        window.toastManager.error(
                            this.getUserFriendlyMessage(0, error),
                            'Retry Failed'
                        );
                    }
                };
                
                retryContainer.appendChild(retryBtn);
                
                // Add recovery options if available
                if (recoveryOptions.length > 0) {
                    const recoveryDiv = document.createElement('div');
                    recoveryDiv.className = 'error-recovery-options';
                    recoveryOptions.forEach(option => {
                        const btn = document.createElement('button');
                        btn.className = 'error-recovery-btn';
                        btn.textContent = option.label;
                        btn.onclick = option.action;
                        recoveryDiv.appendChild(btn);
                    });
                    retryContainer.appendChild(recoveryDiv);
                }
                
                toastElement.appendChild(retryContainer);
            }
            
            return toastId;
        }
        
        // Fallback to console
        console.error('Error:', message);
        return null;
    }

    showError(message, title = 'Error') {
        if (this.isDuplicateError(message)) {
            console.warn('Duplicate error suppressed:', message);
            return;
        }
        try {
            if (window.toastManager && typeof window.toastManager.error === 'function') {
                window.toastManager.error(message, title);
            } else {
                console.error(title + ':', message);
            }
        } catch (e) {
            console.error(title + ':', message);
        }
    }
    
    /**
     * Check if an error message was recently shown (deduplication)
     */
    isDuplicateError(message) {
        const now = Date.now();
        const lastShown = this.recentErrors.get(message);
        
        if (lastShown && (now - lastShown) < this.errorDeduplicationWindow) {
            return true; // This error was shown recently
        }
        
        // Update the timestamp for this error
        this.recentErrors.set(message, now);
        
        // Clean up old entries (older than deduplication window)
        for (const [msg, timestamp] of this.recentErrors.entries()) {
            if (now - timestamp >= this.errorDeduplicationWindow) {
                this.recentErrors.delete(msg);
            }
        }
        
        return false;
    }

    /**
     * Recovery Options
     */
    getRecoveryOptions(status) {
        const options = [];
        
        switch (status) {
            case 0:
                // Connection errors - offer refresh option
                options.push({
                    label: 'Refresh',
                    action: () => {
                        window.location.reload();
                    }
                });
                break;
            case 401:
                options.push({
                    label: 'Go to Login',
                    action: () => {
                        window.location.href = '/auth/login';
                    }
                });
                break;
            case 403:
                options.push({
                    label: 'Go to Dashboard',
                    action: () => {
                        window.location.href = '/main/dashboard';
                    }
                });
                break;
            case 404:
                options.push({
                    label: 'Go to Dashboard',
                    action: () => {
                        window.location.href = '/main/dashboard';
                    }
                });
                options.push({
                    label: 'Go Back',
                    action: () => {
                        window.history.back();
                    }
                });
                break;
            case 500:
            case 502:
            case 503:
            case 504:
                options.push({
                    label: 'Refresh',
                    action: () => {
                        window.location.reload();
                    }
                });
                break;
        }
        
        return options;
    }

    /**
     * Offline Queue Management
     * Stores method, headers, and body in a replay-safe form so POST/PUT replay correctly after JSON round-trip.
     */
    async queueForOffline(url, options, errorId = null) {
        const opts = options || {};
        let method = (opts.method || 'GET').toUpperCase();
        let headers = {};
        let body = null;

        if (opts.headers) {
            if (opts.headers instanceof Headers) {
                opts.headers.forEach((v, k) => { headers[k] = v; });
            } else if (typeof opts.headers === 'object') {
                headers = { ...opts.headers };
            }
        }
        if (opts.body !== undefined && opts.body !== null) {
            if (typeof opts.body === 'string') {
                body = opts.body;
            } else if (opts.body instanceof Blob) {
                try {
                    body = await opts.body.text();
                } catch (e) {
                    console.warn('Offline queue: could not read body as text, skipping queue', e);
                    return;
                }
            } else if (opts.body instanceof ArrayBuffer) {
                body = new TextDecoder().decode(opts.body);
            } else if (typeof opts.body.toString === 'function') {
                body = opts.body.toString();
            } else {
                try {
                    body = JSON.stringify(opts.body);
                } catch (e) {
                    console.warn('Offline queue: could not serialize body, skipping queue', e);
                    return;
                }
            }
        }

        const queueItem = {
            url,
            method,
            headers,
            body,
            errorId,
            timestamp: Date.now(),
            retries: 0
        };

        this.offlineQueue.push(queueItem);
        this.updateOfflineQueueIndicator();
        this.saveOfflineQueue();
    }

    saveOfflineQueue() {
        try {
            localStorage.setItem('offline_queue', JSON.stringify(this.offlineQueue));
        } catch (e) {
            console.warn('Failed to save offline queue:', e);
        }
    }

    loadOfflineQueue() {
        try {
            const stored = localStorage.getItem('offline_queue');
            if (stored) {
                this.offlineQueue = JSON.parse(stored);
                this.updateOfflineQueueIndicator();
            }
        } catch (e) {
            console.warn('Failed to load offline queue:', e);
        }
    }

    async processOfflineQueue() {
        if (this.offlineQueue.length === 0) return;

        const queue = [...this.offlineQueue];
        this.offlineQueue = [];

        for (const item of queue) {
            try {
                let fetchOptions;
                if (item.method !== undefined || item.body !== undefined) {
                    fetchOptions = {
                        method: item.method || 'GET',
                        headers: item.headers || {}
                    };
                    if (item.body != null) {
                        fetchOptions.body = item.body;
                    }
                } else {
                    fetchOptions = item.options || { method: 'GET' };
                }
                const response = await fetch(item.url, fetchOptions);
                if (response.ok && item.errorId) {
                    window.toastManager?.dismiss(item.errorId);
                }
            } catch (error) {
                this.offlineQueue.push(item);
            }
        }

        this.updateOfflineQueueIndicator();
        this.saveOfflineQueue();
    }

    updateOfflineQueueIndicator() {
        const countElement = document.getElementById('offline-queue-count');
        if (countElement) {
            const count = this.offlineQueue.length;
            if (count > 0) {
                countElement.textContent = `(${count} pending)`;
                countElement.style.display = 'inline';
            } else {
                countElement.style.display = 'none';
            }
        }
    }

    setupOfflineQueue() {
        // Load existing queue on init
        this.loadOfflineQueue();
        
        // Process queue when online
        if (this.isOnline && this.offlineQueue.length > 0) {
            this.processOfflineQueue();
        }
    }

    /**
     * Global Error Handlers
     */
    setupGlobalErrorHandlers() {
        // JavaScript errors
        window.addEventListener('error', (event) => {
            this.handleJavaScriptError(event.error, event.message, event.filename, event.lineno);
        });
        
        // Unhandled promise rejections
        window.addEventListener('unhandledrejection', (event) => {
            this.handleUnhandledRejection(event.reason);
        });
    }

    handleJavaScriptError(error, message, filename, lineno) {
        if (this.shouldIgnoreFrontendNoise(error, message)) {
            return;
        }

        const userFriendlyMessage = 'An unexpected error occurred. Please refresh the page or contact support if the problem persists.';
        
        // Check if we've shown this error recently
        if (this.isDuplicateError(userFriendlyMessage)) {
            // Log to console but don't show duplicate toast
            console.error('JavaScript Error (duplicate suppressed):', {
                error,
                message,
                filename,
                lineno
            });
            return;
        }
        
        this.showError(userFriendlyMessage, 'Application Error');
        
        // Log to console for debugging
        console.error('JavaScript Error:', {
            error,
            message,
            filename,
            lineno
        });
    }

    handleUnhandledRejection(reason) {
        if (this.shouldIgnoreFrontendNoise(reason, reason?.message)) {
            return;
        }

        const userFriendlyMessage = 'An operation failed unexpectedly. Please try again or contact support if the problem persists.';
        
        // Check if we've shown this error recently
        if (this.isDuplicateError(userFriendlyMessage)) {
            // Log to console but don't show duplicate toast
            console.error('Unhandled Rejection (duplicate suppressed):', reason);
            return;
        }
        
        this.showError(userFriendlyMessage, 'Operation Failed');
        
        // Log to console for debugging
        console.error('Unhandled Rejection:', reason);
    }

    /**
     * Graceful Degradation
     */
    setupGracefulDegradation() {
        // Check for required features
        this.checkRequiredFeatures();
        
        // Setup feature fallbacks
        this.setupFeatureFallbacks();
    }

    checkRequiredFeatures() {
        // Only check for truly critical features required for app functionality
        // serviceWorker is optional (PWA enhancement) and only works over HTTPS
        const features = {
            'localStorage': typeof Storage !== 'undefined',
            'fetch': typeof fetch !== 'undefined'
        };
        
        const missing = Object.entries(features)
            .filter(([_, available]) => !available)
            .map(([name]) => name);
        
        if (missing.length > 0) {
            console.warn('Missing features:', missing);
            this.showError(
                `Some features may not work properly: ${missing.join(', ')}. Please update your browser.`,
                'Browser Compatibility'
            );
        }
        
        // Log serviceWorker availability for debugging (but don't show warning)
        if (!('serviceWorker' in navigator)) {
            const isSecureContext = window.isSecureContext || location.protocol === 'https:' || location.hostname === 'localhost' || location.hostname === '127.0.0.1';
            if (!isSecureContext) {
                console.debug('ServiceWorker not available: requires HTTPS (or localhost)');
            } else {
                console.debug('ServiceWorker not available: browser does not support it');
            }
        }
    }

    setupFeatureFallbacks() {
        // Fallback for fetch if not available
        if (typeof fetch === 'undefined') {
            console.warn('Fetch API not available, using XMLHttpRequest fallback');
            this.polyfillFetch();
        }
        
        // Fallback for localStorage
        if (typeof Storage === 'undefined' || typeof localStorage === 'undefined') {
            console.warn('LocalStorage not available, using memory storage');
            this.polyfillLocalStorage();
        }
    }

    polyfillFetch() {
        // Simple fetch polyfill using XMLHttpRequest
        window.fetch = function(url, options = {}) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                const method = options.method || 'GET';
                const headers = options.headers || {};
                
                xhr.open(method, url, true);
                
                // Set headers
                Object.keys(headers).forEach(key => {
                    xhr.setRequestHeader(key, headers[key]);
                });
                
                // Handle response
                xhr.onload = function() {
                    const response = {
                        ok: xhr.status >= 200 && xhr.status < 300,
                        status: xhr.status,
                        statusText: xhr.statusText,
                        headers: {
                            get: function(name) {
                                return xhr.getResponseHeader(name);
                            }
                        },
                        text: function() {
                            return Promise.resolve(xhr.responseText);
                        },
                        json: function() {
                            try {
                                return Promise.resolve(JSON.parse(xhr.responseText));
                            } catch (e) {
                                return Promise.reject(new Error('Invalid JSON response'));
                            }
                        },
                        blob: function() {
                            return Promise.resolve(new Blob([xhr.response]));
                        },
                        arrayBuffer: function() {
                            return Promise.resolve(xhr.response);
                        }
                    };
                    
                    if (xhr.status >= 200 && xhr.status < 300) {
                        resolve(response);
                    } else {
                        reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
                    }
                };
                
                xhr.onerror = function() {
                    reject(new Error('Network request failed'));
                };
                
                xhr.ontimeout = function() {
                    reject(new Error('Request timeout'));
                };
                
                // Set timeout if specified
                if (options.timeout) {
                    xhr.timeout = options.timeout;
                }
                
                // Send request
                if (options.body) {
                    if (typeof options.body === 'string') {
                        xhr.send(options.body);
                    } else if (options.body instanceof FormData) {
                        xhr.send(options.body);
                    } else {
                        xhr.send(JSON.stringify(options.body));
                    }
                } else {
                    xhr.send();
                }
            });
        };
    }

    polyfillLocalStorage() {
        // In-memory storage fallback
        const memoryStorage = {};
        
        window.localStorage = {
            getItem: function(key) {
                return memoryStorage[key] || null;
            },
            setItem: function(key, value) {
                try {
                    memoryStorage[key] = String(value);
                    // Dispatch storage event for compatibility
                    window.dispatchEvent(new Event('storage'));
                } catch (e) {
                    console.warn('Memory storage setItem failed:', e);
                }
            },
            removeItem: function(key) {
                try {
                    delete memoryStorage[key];
                    window.dispatchEvent(new Event('storage'));
                } catch (e) {
                    console.warn('Memory storage removeItem failed:', e);
                }
            },
            clear: function() {
                try {
                    Object.keys(memoryStorage).forEach(key => {
                        delete memoryStorage[key];
                    });
                    window.dispatchEvent(new Event('storage'));
                } catch (e) {
                    console.warn('Memory storage clear failed:', e);
                }
            },
            get length() {
                return Object.keys(memoryStorage).length;
            },
            key: function(index) {
                const keys = Object.keys(memoryStorage);
                return keys[index] || null;
            }
        };
        
        // Also polyfill sessionStorage with same in-memory implementation
        if (typeof sessionStorage === 'undefined') {
            window.sessionStorage = Object.create(window.localStorage);
        }
    }

    retryFailedOperations() {
        // Retry operations from retry queue
        const queue = [...this.retryQueue];
        this.retryQueue = [];
        
        queue.forEach(operation => {
            try {
                operation();
            } catch (error) {
                console.error('Failed to retry operation:', error);
            }
        });
    }

    shouldIgnoreFrontendNoise(error, message) {
        const normalizedMessage = String(message || error?.message || '').toLowerCase();

        // Known benign ResizeObserver warning triggered by various UI libraries/browsers
        if (normalizedMessage.includes('resizeobserver loop limit exceeded') ||
            normalizedMessage.includes('resizeobserver loop completed with undelivered notifications')) {
            console.debug('Ignored benign ResizeObserver warning:', message || error);
            return true;
        }

        return false;
    }
}

// Initialize enhanced error handler
window.enhancedErrorHandler = new EnhancedErrorHandler();

// Remove offline indicator when online
document.addEventListener('DOMContentLoaded', () => {
    if (navigator.onLine) {
        const indicator = document.getElementById('offline-indicator');
        if (indicator) {
            indicator.remove();
        }
    }
});

