/* ============================================
   RSL Loader Component
   Version: 2.0.0
   Description: Premium loading indicators with
   multiple variants, progress tracking, and
   full accessibility support.

   Features:
   - 7 spinner variants (spinner, dual, dots, bars, pulse, ripple, square)
   - 2 progress types (circular, bar)
   - 5 modes (fullscreen, overlay, inline, button, center)
   - 4 sizes (sm, md, lg, xl)
   - 7 colors (primary, success, warning, danger, info, light, dark)
   - Success/error state transitions
   - Full WCAG 2.1 AA accessibility
   - FilterBus integration for loading state management
   ============================================ */

(function() {
    'use strict';

    // Initialize RSL namespace
    window.RSL = window.RSL || {};

    /**
     * Loader Component
     * Creates and manages loading indicators
     */
    window.RSL.Loader = {

        /**
         * Track active loaders
         * @private
         */
        _instances: new Map(),

        /**
         * Counter for unique IDs
         * @private
         */
        _idCounter: 0,

        /**
         * Screen reader live region
         * @private
         */
        _liveRegion: null,

        /**
         * Default configuration
         */
        defaults: {
            variant: 'spinner',
            mode: 'inline',
            size: 'md',
            color: 'primary',
            message: null,
            progress: null,
            dismissible: false,
            dismissText: 'Cancel',
            autoHide: null,
            onShow: null,
            onHide: null,
            onDismiss: null,
            onProgress: null
        },

        /**
         * Initialize all loaders with data attributes
         */
        init: function() {
            var self = this;

            // Create live region for screen reader announcements
            this._createLiveRegion();

            // Find all loaders with data attributes
            var loaders = document.querySelectorAll('[data-rsl-loader]');
            loaders.forEach(function(element) {
                if (!element.hasAttribute('data-loader-initialized')) {
                    self.create(element);
                }
            });

            // Subscribe to FilterBus for loading state management
            if (window.RSL.FilterBus) {
                window.RSL.FilterBus.subscribe('rsl-loader-state', function(state) {
                    if (state.loading) {
                        self.show(state.target || 'body', state.options || {});
                    } else {
                        self.hide(state.target || 'body');
                    }
                }, { keys: ['loading'] });
            }
        },

        /**
         * Create a loader instance from an element or options
         * @param {HTMLElement|string|Object} target - Element, selector, or options
         * @param {Object} options - Configuration options
         * @returns {Object} Loader instance
         */
        create: function(target, options) {
            var element;
            var config = Object.assign({}, this.defaults);

            // Handle different argument types
            if (typeof target === 'string') {
                element = document.querySelector(target);
            } else if (target instanceof HTMLElement) {
                element = target;
            } else if (typeof target === 'object' && !target.nodeType) {
                // Options object passed directly
                options = target;
                element = null;
            }

            // Merge options
            if (options) {
                Object.assign(config, options);
            }

            // Extract data attributes if element exists
            if (element) {
                this._extractDataAttributes(element, config);
            }

            // Create the loader element
            var loader = this._createLoaderElement(config);

            // Generate unique ID
            var id = 'rsl-loader-' + (++this._idCounter);
            loader.id = id;

            // Store instance
            var instance = {
                id: id,
                element: loader,
                config: config,
                target: element,
                isVisible: false,
                progress: config.progress
            };

            this._instances.set(id, instance);

            // Mark original element as initialized
            if (element) {
                element.setAttribute('data-loader-initialized', 'true');
                element.setAttribute('data-loader-id', id);

                // If element is a container, add relative positioning
                if (config.mode === 'overlay') {
                    element.setAttribute('data-rsl-loader-container', 'true');
                }
            }

            return instance;
        },

        /**
         * Show a loader
         * @param {string|HTMLElement|Object} target - Target element/selector or options
         * @param {Object} options - Configuration options
         * @returns {Object} Loader instance
         */
        show: function(target, options) {
            var instance;
            var config = Object.assign({}, this.defaults, options || {});

            // Check if we have an existing instance
            if (typeof target === 'string') {
                var element = document.querySelector(target);
                if (element && element.hasAttribute('data-loader-id')) {
                    instance = this._instances.get(element.getAttribute('data-loader-id'));
                }
            }

            // Create new instance if needed
            if (!instance) {
                instance = this.create(target, config);
            }

            // Update config if options provided
            if (options) {
                Object.assign(instance.config, options);
                this._updateLoaderElement(instance);
            }

            // Determine where to append the loader
            var parent;
            if (instance.config.mode === 'fullscreen') {
                parent = document.body;
            } else if (instance.target) {
                parent = instance.target;
            } else {
                parent = document.body;
            }

            // Add to DOM
            parent.appendChild(instance.element);
            instance.isVisible = true;

            // Add entering animation
            instance.element.classList.add('rsl-loader-entering');
            setTimeout(function() {
                instance.element.classList.remove('rsl-loader-entering');
            }, 200);

            // Announce to screen readers
            this._announce('Loading started' + (instance.config.message ? ': ' + instance.config.message : ''));

            // Set aria-busy on target
            if (instance.target && instance.config.mode === 'overlay') {
                instance.target.setAttribute('aria-busy', 'true');
            }

            // Handle auto-hide
            if (instance.config.autoHide) {
                var self = this;
                setTimeout(function() {
                    self.hide(instance);
                }, instance.config.autoHide);
            }

            // Focus management for fullscreen mode
            if (instance.config.mode === 'fullscreen') {
                this._trapFocus(instance);
            }

            // Callback
            if (typeof instance.config.onShow === 'function') {
                instance.config.onShow(instance);
            }

            return instance;
        },

        /**
         * Hide a loader
         * @param {string|HTMLElement|Object} target - Target element/selector or instance
         * @param {Object} options - Options (e.g., { success: true, message: 'Done!' })
         */
        hide: function(target, options) {
            var instance = this._getInstance(target);
            if (!instance || !instance.isVisible) return;

            options = options || {};
            var self = this;

            // Show success or error state before hiding
            if (options.success || options.error) {
                this._showResultState(instance, options.success ? 'success' : 'error', options.message);

                setTimeout(function() {
                    self._doHide(instance);
                }, 1500);
            } else {
                this._doHide(instance);
            }
        },

        /**
         * Internal hide method
         * @private
         */
        _doHide: function(instance) {
            var self = this;

            // Add exiting animation
            instance.element.classList.add('rsl-loader-exiting');

            setTimeout(function() {
                // Remove from DOM
                if (instance.element.parentNode) {
                    instance.element.parentNode.removeChild(instance.element);
                }
                instance.isVisible = false;
                instance.element.classList.remove('rsl-loader-exiting');

                // Remove aria-busy from target
                if (instance.target) {
                    instance.target.removeAttribute('aria-busy');
                }

                // Announce to screen readers
                self._announce('Loading complete');

                // Restore focus
                if (instance.config.mode === 'fullscreen' && instance._previousFocus) {
                    instance._previousFocus.focus();
                }

                // Callback
                if (typeof instance.config.onHide === 'function') {
                    instance.config.onHide(instance);
                }
            }, 200);
        },

        /**
         * Update progress value
         * @param {string|HTMLElement|Object} target - Target
         * @param {number} value - Progress value (0-100)
         */
        setProgress: function(target, value) {
            var instance = this._getInstance(target);
            if (!instance) return;

            value = Math.max(0, Math.min(100, value));
            instance.progress = value;

            // Update circular progress
            var circle = instance.element.querySelector('.rsl-loader-progress-circle .progress');
            if (circle) {
                var circumference = 126; // 2 * PI * r (r=20)
                var offset = circumference - (value / 100) * circumference;
                circle.style.strokeDashoffset = offset;
            }

            // Update linear progress bar
            var bar = instance.element.querySelector('.rsl-loader-progress-bar .fill');
            if (bar) {
                bar.style.width = value + '%';
            }

            // Update percentage display
            var valueEl = instance.element.querySelector('.rsl-loader-progress-circle .value');
            if (valueEl) {
                valueEl.textContent = Math.round(value) + '%';
            }

            // Update ARIA
            var progressEl = instance.element.querySelector('[role="progressbar"]');
            if (progressEl) {
                progressEl.setAttribute('aria-valuenow', value);
            }

            // Announce milestone progress
            if (value % 25 === 0 && value > 0) {
                this._announce(value + '% complete');
            }

            // Callback
            if (typeof instance.config.onProgress === 'function') {
                instance.config.onProgress(instance, value);
            }
        },

        /**
         * Update message text
         * @param {string|HTMLElement|Object} target - Target
         * @param {string} message - New message
         */
        setMessage: function(target, message) {
            var instance = this._getInstance(target);
            if (!instance) return;

            instance.config.message = message;

            var messageEl = instance.element.querySelector('.rsl-loader-message');
            if (messageEl) {
                messageEl.textContent = message;
            } else if (message) {
                // Create message element
                messageEl = document.createElement('div');
                messageEl.className = 'rsl-loader-message';
                messageEl.textContent = message;
                messageEl.setAttribute('aria-live', 'polite');
                instance.element.appendChild(messageEl);
            }

            // Announce to screen readers
            if (message) {
                this._announce(message);
            }
        },

        /**
         * Show success state
         * @param {string|HTMLElement|Object} target - Target
         * @param {string} message - Optional success message
         */
        success: function(target, message) {
            this.hide(target, { success: true, message: message || 'Complete!' });
        },

        /**
         * Show error state
         * @param {string|HTMLElement|Object} target - Target
         * @param {string} message - Optional error message
         */
        error: function(target, message) {
            this.hide(target, { error: true, message: message || 'Error occurred' });
        },

        /**
         * Destroy a loader instance
         * @param {string|HTMLElement|Object} target - Target
         */
        destroy: function(target) {
            var instance = this._getInstance(target);
            if (!instance) return;

            // Remove from DOM if visible
            if (instance.isVisible && instance.element.parentNode) {
                instance.element.parentNode.removeChild(instance.element);
            }

            // Clean up target element
            if (instance.target) {
                instance.target.removeAttribute('data-loader-initialized');
                instance.target.removeAttribute('data-loader-id');
                instance.target.removeAttribute('data-rsl-loader-container');
                instance.target.removeAttribute('aria-busy');
            }

            // Remove from instances
            this._instances.delete(instance.id);
        },

        /**
         * Get a loader instance
         * @private
         */
        _getInstance: function(target) {
            // If already an instance object
            if (target && target.id && this._instances.has(target.id)) {
                return target;
            }

            // If element with data-loader-id
            var element;
            if (typeof target === 'string') {
                element = document.querySelector(target);
            } else if (target instanceof HTMLElement) {
                element = target;
            }

            if (element && element.hasAttribute('data-loader-id')) {
                return this._instances.get(element.getAttribute('data-loader-id'));
            }

            // Check if target matches any instance target
            var found = null;
            this._instances.forEach(function(instance) {
                if (instance.target === element) {
                    found = instance;
                }
            });

            return found;
        },

        /**
         * Extract data attributes from element
         * @private
         */
        _extractDataAttributes: function(element, config) {
            var attrs = {
                'data-loader-variant': 'variant',
                'data-loader-mode': 'mode',
                'data-loader-size': 'size',
                'data-loader-color': 'color',
                'data-loader-message': 'message',
                'data-loader-progress': 'progress',
                'data-loader-dismissible': 'dismissible',
                'data-loader-dismiss-text': 'dismissText',
                'data-loader-auto-hide': 'autoHide'
            };

            for (var attr in attrs) {
                if (element.hasAttribute(attr)) {
                    var value = element.getAttribute(attr);
                    var key = attrs[attr];

                    // Convert types
                    if (key === 'progress' || key === 'autoHide') {
                        config[key] = parseInt(value, 10);
                    } else if (key === 'dismissible') {
                        config[key] = value === 'true' || value === '';
                    } else {
                        config[key] = value;
                    }
                }
            }
        },

        /**
         * Create the loader DOM element
         * @private
         */
        _createLoaderElement: function(config) {
            var loader = document.createElement('div');
            loader.className = 'rsl-loader';

            // Add mode class
            if (config.mode) {
                loader.classList.add('rsl-loader-' + config.mode);
            }

            // Add size class
            if (config.size && config.size !== 'md') {
                loader.classList.add('rsl-loader-' + config.size);
            }

            // Add color class
            if (config.color) {
                loader.classList.add('rsl-loader-' + config.color);
            }

            // Add ARIA attributes
            loader.setAttribute('role', 'status');
            loader.setAttribute('aria-live', 'polite');

            // Create spinner element based on variant
            var spinner = this._createSpinnerElement(config.variant, config.progress);
            loader.appendChild(spinner);

            // Add message if provided
            if (config.message) {
                var message = document.createElement('div');
                message.className = 'rsl-loader-message';
                message.textContent = config.message;
                loader.appendChild(message);
            }

            // Add dismiss button if dismissible
            if (config.dismissible) {
                var dismiss = document.createElement('button');
                dismiss.className = 'rsl-loader-dismiss';
                dismiss.textContent = config.dismissText || 'Cancel';
                dismiss.setAttribute('type', 'button');
                dismiss.setAttribute('aria-label', 'Cancel loading');

                var self = this;
                dismiss.addEventListener('click', function() {
                    var instance = self._instances.get(loader.id);
                    if (instance) {
                        if (typeof instance.config.onDismiss === 'function') {
                            instance.config.onDismiss(instance);
                        }
                        self.hide(instance);
                    }
                });

                loader.appendChild(dismiss);
            }

            return loader;
        },

        /**
         * Create spinner element based on variant
         * @private
         */
        _createSpinnerElement: function(variant, progress) {
            var container;

            switch (variant) {
                case 'dual':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-dual';
                    container.setAttribute('aria-hidden', 'true');
                    break;

                case 'dots':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-dots';
                    container.setAttribute('aria-hidden', 'true');
                    for (var i = 0; i < 3; i++) {
                        var dot = document.createElement('div');
                        dot.className = 'dot';
                        container.appendChild(dot);
                    }
                    break;

                case 'bars':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-bars';
                    container.setAttribute('aria-hidden', 'true');
                    for (var j = 0; j < 5; j++) {
                        var bar = document.createElement('div');
                        bar.className = 'bar';
                        container.appendChild(bar);
                    }
                    break;

                case 'pulse':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-pulse';
                    container.setAttribute('aria-hidden', 'true');
                    break;

                case 'ripple':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-ripple';
                    container.setAttribute('aria-hidden', 'true');
                    for (var k = 0; k < 2; k++) {
                        var ripple = document.createElement('div');
                        ripple.className = 'ripple';
                        container.appendChild(ripple);
                    }
                    break;

                case 'square':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-square';
                    container.setAttribute('aria-hidden', 'true');
                    break;

                case 'progress-circle':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-progress-circle';
                    container.setAttribute('role', 'progressbar');
                    container.setAttribute('aria-valuemin', '0');
                    container.setAttribute('aria-valuemax', '100');
                    container.setAttribute('aria-valuenow', progress || 0);

                    var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
                    svg.setAttribute('viewBox', '0 0 44 44');

                    var track = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                    track.setAttribute('class', 'track');
                    track.setAttribute('cx', '22');
                    track.setAttribute('cy', '22');
                    track.setAttribute('r', '20');

                    var progressCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                    progressCircle.setAttribute('class', 'progress');
                    progressCircle.setAttribute('cx', '22');
                    progressCircle.setAttribute('cy', '22');
                    progressCircle.setAttribute('r', '20');

                    // Set initial progress
                    if (progress !== null && progress !== undefined) {
                        var circumference = 126;
                        var offset = circumference - (progress / 100) * circumference;
                        progressCircle.style.strokeDashoffset = offset;
                    }

                    svg.appendChild(track);
                    svg.appendChild(progressCircle);
                    container.appendChild(svg);

                    // Add percentage text
                    var value = document.createElement('div');
                    value.className = 'value';
                    value.textContent = (progress || 0) + '%';
                    container.appendChild(value);
                    break;

                case 'progress-bar':
                    container = document.createElement('div');
                    container.className = 'rsl-loader-progress-bar';
                    if (progress === null || progress === undefined) {
                        container.classList.add('indeterminate');
                    }
                    container.setAttribute('role', 'progressbar');
                    container.setAttribute('aria-valuemin', '0');
                    container.setAttribute('aria-valuemax', '100');
                    container.setAttribute('aria-valuenow', progress || 0);

                    var fill = document.createElement('div');
                    fill.className = 'fill';
                    if (progress !== null && progress !== undefined) {
                        fill.style.width = progress + '%';
                    }
                    container.appendChild(fill);
                    break;

                case 'spinner':
                default:
                    container = document.createElement('div');
                    container.className = 'rsl-loader-spinner';
                    container.setAttribute('aria-hidden', 'true');
                    break;
            }

            return container;
        },

        /**
         * Update an existing loader element
         * @private
         */
        _updateLoaderElement: function(instance) {
            var loader = instance.element;
            var config = instance.config;

            // Update classes
            loader.className = 'rsl-loader';

            if (config.mode) {
                loader.classList.add('rsl-loader-' + config.mode);
            }
            if (config.size && config.size !== 'md') {
                loader.classList.add('rsl-loader-' + config.size);
            }
            if (config.color) {
                loader.classList.add('rsl-loader-' + config.color);
            }

            // Update spinner variant - remove existing and create new
            var existingSpinner = loader.querySelector(
                '.rsl-loader-spinner, .rsl-loader-dual, .rsl-loader-dots, ' +
                '.rsl-loader-bars, .rsl-loader-pulse, .rsl-loader-ripple, ' +
                '.rsl-loader-square, .rsl-loader-progress-circle, .rsl-loader-progress-bar'
            );
            if (existingSpinner) {
                existingSpinner.remove();
            }

            // Create new spinner with current variant
            var newSpinner = this._createSpinnerElement(config.variant, config.progress);
            loader.insertBefore(newSpinner, loader.firstChild);

            // Update message
            var messageEl = loader.querySelector('.rsl-loader-message');
            if (config.message) {
                if (messageEl) {
                    messageEl.textContent = config.message;
                } else {
                    messageEl = document.createElement('div');
                    messageEl.className = 'rsl-loader-message';
                    messageEl.textContent = config.message;
                    loader.appendChild(messageEl);
                }
            } else if (messageEl) {
                messageEl.remove();
            }
        },

        /**
         * Show success or error state
         * @private
         */
        _showResultState: function(instance, type, message) {
            var loader = instance.element;

            // Remove existing spinner
            var existingSpinner = loader.querySelector(
                '.rsl-loader-spinner, .rsl-loader-dual, .rsl-loader-dots, ' +
                '.rsl-loader-bars, .rsl-loader-pulse, .rsl-loader-ripple, ' +
                '.rsl-loader-square, .rsl-loader-progress-circle, .rsl-loader-progress-bar'
            );
            if (existingSpinner) {
                existingSpinner.remove();
            }

            // Create result icon
            var icon = document.createElement('div');
            icon.className = type === 'success' ? 'rsl-loader-success-icon' : 'rsl-loader-error-icon';

            var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            svg.setAttribute('viewBox', '0 0 24 24');
            svg.setAttribute('fill', 'none');
            svg.setAttribute('stroke', 'currentColor');
            svg.setAttribute('stroke-width', '3');
            svg.setAttribute('stroke-linecap', 'round');
            svg.setAttribute('stroke-linejoin', 'round');

            var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            if (type === 'success') {
                path.setAttribute('d', 'M20 6L9 17l-5-5');
            } else {
                path.setAttribute('d', 'M18 6L6 18M6 6l12 12');
            }
            svg.appendChild(path);
            icon.appendChild(svg);

            // Insert at beginning
            loader.insertBefore(icon, loader.firstChild);

            // Update message
            if (message) {
                this.setMessage(instance, message);
            }

            // Announce
            this._announce(type === 'success' ? 'Loading successful' : 'Loading failed');
        },

        /**
         * Create live region for screen reader announcements
         * @private
         */
        _createLiveRegion: function() {
            if (this._liveRegion) return;

            this._liveRegion = document.createElement('div');
            this._liveRegion.setAttribute('aria-live', 'polite');
            this._liveRegion.setAttribute('aria-atomic', 'true');
            this._liveRegion.className = 'rsl-sr-only';
            this._liveRegion.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;';

            document.body.appendChild(this._liveRegion);
        },

        /**
         * Announce message to screen readers
         * @private
         */
        _announce: function(message) {
            if (!this._liveRegion) {
                this._createLiveRegion();
            }

            // Clear and set new message (triggers announcement)
            this._liveRegion.textContent = '';
            setTimeout(function() {
                this._liveRegion.textContent = message;
            }.bind(this), 100);
        },

        /**
         * Trap focus within fullscreen loader
         * @private
         */
        _trapFocus: function(instance) {
            var loader = instance.element;

            // Store previous focus
            instance._previousFocus = document.activeElement;

            // Find focusable elements
            var focusable = loader.querySelectorAll(
                'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
            );

            if (focusable.length === 0) {
                // Make loader itself focusable
                loader.setAttribute('tabindex', '-1');
                loader.focus();
                return;
            }

            var firstFocusable = focusable[0];
            var lastFocusable = focusable[focusable.length - 1];

            // Focus first element
            firstFocusable.focus();

            // Handle Tab key
            loader.addEventListener('keydown', function(e) {
                if (e.key !== 'Tab') return;

                if (e.shiftKey) {
                    if (document.activeElement === firstFocusable) {
                        e.preventDefault();
                        lastFocusable.focus();
                    }
                } else {
                    if (document.activeElement === lastFocusable) {
                        e.preventDefault();
                        firstFocusable.focus();
                    }
                }
            });

            // Handle Escape key for dismissible loaders
            if (instance.config.dismissible) {
                loader.addEventListener('keydown', function(e) {
                    if (e.key === 'Escape') {
                        e.preventDefault();
                        if (typeof instance.config.onDismiss === 'function') {
                            instance.config.onDismiss(instance);
                        }
                        RSL.Loader.hide(instance);
                    }
                });
            }
        }
    };

    // Auto-initialize on DOM ready
    document.addEventListener('DOMContentLoaded', function() {
        window.RSL.Loader.init();
    });

})();
