/**
 * RSL Star Rating Component
 * Version: 1.0.0
 *
 * A fully accessible star rating component supporting:
 * - Input mode (user selects rating)
 * - Display mode (readonly aggregate ratings)
 * - Filter mode (FilterBus integration for filtering)
 *
 * WCAG 2.1 AA Compliant:
 * - Full keyboard navigation (arrow keys, Home/End)
 * - Screen reader announcements via aria-live
 * - Role="slider" with proper ARIA attributes
 * - Visible focus indicators
 * - Respects prefers-reduced-motion
 *
 * @namespace RSL.StarRating
 */

(function() {
    'use strict';

    // Namespace
    window.RSL = window.RSL || {};

    // Track initialized instances
    const instances = new Set();

    // SVG Icons
    const ICONS = {
        star: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>',
        heart: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>',
        thumb: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>',
        close: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" fill="currentColor"/></svg>'
    };

    /**
     * Default configuration
     */
    const DEFAULTS = {
        max: 5,
        value: 0,
        half: true,
        readonly: false,
        size: 'medium',
        icon: 'star',
        showValue: false,
        clearable: false,
        name: null,
        required: false,
        label: null
    };

    /**
     * Get configuration from data attributes
     * @param {HTMLElement} element - The star rating element
     * @returns {Object} Configuration object
     */
    function getConfig(element) {
        return {
            max: parseInt(element.dataset.starMax, 10) || DEFAULTS.max,
            value: parseFloat(element.dataset.starValue) || DEFAULTS.value,
            half: element.dataset.starHalf !== 'false',
            readonly: element.dataset.starReadonly === 'true',
            size: element.dataset.starSize || DEFAULTS.size,
            icon: element.dataset.starIcon || DEFAULTS.icon,
            showValue: element.dataset.starShowValue === 'true',
            clearable: element.dataset.starClearable === 'true',
            name: element.dataset.starName || DEFAULTS.name,
            required: element.dataset.starRequired === 'true',
            label: element.dataset.starLabel || DEFAULTS.label,
            color: element.dataset.starColor || null,
            emptyColor: element.dataset.starEmptyColor || null,
            filterField: element.dataset.filterbusField || null
        };
    }

    /**
     * Get the appropriate icon SVG
     * @param {string} iconName - Name of the icon
     * @returns {string} SVG markup
     */
    function getIcon(iconName) {
        return ICONS[iconName] || ICONS.star;
    }

    /**
     * Create the star rating structure
     * @param {HTMLElement} element - The container element
     * @param {Object} config - Configuration object
     */
    function buildStarRating(element, config) {
        // Clear existing content
        element.innerHTML = '';

        // Apply custom colors via CSS variables
        if (config.color) {
            element.style.setProperty('--star-color', config.color);
        }
        if (config.emptyColor) {
            element.style.setProperty('--star-empty-color', config.emptyColor);
        }

        // Add required class if needed
        if (config.required) {
            element.classList.add('rsl-star-rating-required');
        }

        // Create label if provided
        if (config.label) {
            const labelEl = document.createElement('span');
            labelEl.className = 'rsl-star-rating-label';
            labelEl.textContent = config.label;
            labelEl.id = 'star-label-' + generateId();
            element.appendChild(labelEl);
            element.setAttribute('aria-labelledby', labelEl.id);
        }

        // Create stars container
        const starsContainer = document.createElement('div');
        starsContainer.className = 'rsl-star-rating-stars';
        starsContainer.setAttribute('role', 'group');

        // Create individual stars
        for (let i = 1; i <= config.max; i++) {
            const star = document.createElement('span');
            star.className = 'rsl-star';
            star.dataset.value = i;
            star.innerHTML = getIcon(config.icon);

            // For half-star support, add overlay container
            if (config.half) {
                const halfOverlay = document.createElement('span');
                halfOverlay.className = 'rsl-star-half-overlay';
                halfOverlay.innerHTML = getIcon(config.icon);
                star.appendChild(halfOverlay);
            }

            starsContainer.appendChild(star);
        }

        element.appendChild(starsContainer);

        // Create value display if enabled
        if (config.showValue) {
            const valueDisplay = document.createElement('span');
            valueDisplay.className = 'rsl-star-rating-value';
            valueDisplay.setAttribute('aria-hidden', 'true');
            element.appendChild(valueDisplay);
        }

        // Create clear button if enabled
        if (config.clearable && !config.readonly) {
            const clearBtn = document.createElement('button');
            clearBtn.type = 'button';
            clearBtn.className = 'rsl-star-rating-clear';
            clearBtn.innerHTML = ICONS.close;
            clearBtn.setAttribute('aria-label', 'Clear rating');
            clearBtn.setAttribute('tabindex', '-1');
            element.appendChild(clearBtn);
        }

        // Create hidden input for form submission
        if (config.name) {
            const hiddenInput = document.createElement('input');
            hiddenInput.type = 'hidden';
            hiddenInput.name = config.name;
            hiddenInput.value = config.value;
            if (config.required) {
                hiddenInput.required = true;
            }
            element.appendChild(hiddenInput);
        }

        // Create screen reader announcement region
        const srAnnounce = document.createElement('span');
        srAnnounce.className = 'rsl-star-rating-sr-only';
        srAnnounce.setAttribute('role', 'status');
        srAnnounce.setAttribute('aria-live', 'polite');
        srAnnounce.setAttribute('aria-atomic', 'true');
        element.appendChild(srAnnounce);

        // Set ARIA attributes
        if (!config.readonly) {
            element.setAttribute('role', 'slider');
            element.setAttribute('aria-valuemin', '0');
            element.setAttribute('aria-valuemax', String(config.max));
            element.setAttribute('aria-valuenow', String(config.value));
            element.setAttribute('aria-valuetext', getValueText(config.value, config.max, config.icon));
            element.setAttribute('tabindex', '0');
        } else {
            element.setAttribute('role', 'img');
            element.setAttribute('aria-label', getValueText(config.value, config.max, config.icon));
        }

        // Update visual state
        updateStars(element, config.value, config);
    }

    /**
     * Generate a unique ID
     * @returns {string} Unique ID
     */
    function generateId() {
        return Math.random().toString(36).substr(2, 9);
    }

    /**
     * Get accessible value text
     * @param {number} value - Current value
     * @param {number} max - Maximum value
     * @param {string} icon - Icon type
     * @returns {string} Accessible text
     */
    function getValueText(value, max, icon) {
        const iconName = icon === 'heart' ? 'hearts' : icon === 'thumb' ? 'thumbs' : 'stars';
        if (value === 0) {
            return `No rating selected. ${max} ${iconName} available.`;
        }
        return `${value} out of ${max} ${iconName}`;
    }

    /**
     * Update star visual states
     * @param {HTMLElement} element - The star rating element
     * @param {number} value - Current value
     * @param {Object} config - Configuration object
     */
    function updateStars(element, value, config) {
        const stars = element.querySelectorAll('.rsl-star');
        const wholeValue = Math.floor(value);
        const hasHalf = config.half && value % 1 >= 0.5;

        stars.forEach((star, index) => {
            const starValue = index + 1;

            // Remove all state classes
            star.classList.remove('rsl-star-filled', 'rsl-star-half', 'rsl-star-hover-preview');

            if (starValue <= wholeValue) {
                star.classList.add('rsl-star-filled');
            } else if (starValue === wholeValue + 1 && hasHalf) {
                star.classList.add('rsl-star-half');
            }
        });

        // Update value display
        const valueDisplay = element.querySelector('.rsl-star-rating-value');
        if (valueDisplay) {
            valueDisplay.textContent = value > 0 ? value.toFixed(config.half ? 1 : 0) : '—';
        }

        // Update hidden input
        const hiddenInput = element.querySelector('input[type="hidden"]');
        if (hiddenInput) {
            hiddenInput.value = value;
        }

        // Update ARIA
        element.setAttribute('aria-valuenow', String(value));
        element.setAttribute('aria-valuetext', getValueText(value, config.max, config.icon));
    }

    /**
     * Announce to screen readers
     * @param {HTMLElement} element - The star rating element
     * @param {string} message - Message to announce
     */
    function announce(element, message) {
        const srAnnounce = element.querySelector('.rsl-star-rating-sr-only[aria-live]');
        if (srAnnounce) {
            srAnnounce.textContent = message;
        }
    }

    /**
     * Calculate value from mouse position
     * @param {HTMLElement} element - The star rating element
     * @param {MouseEvent} event - The mouse event
     * @param {Object} config - Configuration object
     * @returns {number} Calculated value
     */
    function calculateValueFromMouse(element, event, config) {
        const starsContainer = element.querySelector('.rsl-star-rating-stars');
        const stars = starsContainer.querySelectorAll('.rsl-star');
        const containerRect = starsContainer.getBoundingClientRect();

        // Find which star was clicked/hovered
        for (let i = stars.length - 1; i >= 0; i--) {
            const star = stars[i];
            const starRect = star.getBoundingClientRect();

            if (event.clientX >= starRect.left) {
                const starValue = i + 1;

                if (config.half) {
                    // Check if click is in left or right half
                    const starMiddle = starRect.left + starRect.width / 2;
                    if (event.clientX < starMiddle) {
                        return starValue - 0.5;
                    }
                }
                return starValue;
            }
        }

        return 0;
    }

    /**
     * Handle hover preview
     * @param {HTMLElement} element - The star rating element
     * @param {number} hoverValue - Value to preview
     * @param {Object} config - Configuration object
     */
    function showHoverPreview(element, hoverValue, config) {
        const stars = element.querySelectorAll('.rsl-star');
        const wholeHover = Math.floor(hoverValue);
        const hasHalfHover = config.half && hoverValue % 1 >= 0.5;

        stars.forEach((star, index) => {
            const starValue = index + 1;

            star.classList.remove('rsl-star-hover-preview');

            if (starValue <= wholeHover || (starValue === wholeHover + 1 && hasHalfHover)) {
                star.classList.add('rsl-star-hover-preview');
            }
        });
    }

    /**
     * Clear hover preview
     * @param {HTMLElement} element - The star rating element
     */
    function clearHoverPreview(element) {
        const stars = element.querySelectorAll('.rsl-star');
        stars.forEach(star => star.classList.remove('rsl-star-hover-preview'));
    }

    /**
     * Set the rating value
     * @param {HTMLElement} element - The star rating element
     * @param {number} value - New value
     * @param {boolean} silent - If true, don't fire events
     */
    function setValue(element, value, silent) {
        const config = getConfig(element);
        const oldValue = parseFloat(element.dataset.starValue) || 0;

        // Clamp value
        value = Math.max(0, Math.min(config.max, value));

        // Round to half or whole based on config
        if (!config.half) {
            value = Math.round(value);
        } else {
            value = Math.round(value * 2) / 2;
        }

        // Update data attribute
        element.dataset.starValue = value;

        // Update visuals
        updateStars(element, value, config);

        // Announce to screen readers
        if (!silent && value !== oldValue) {
            announce(element, getValueText(value, config.max, config.icon));
        }

        // Fire change event
        if (!silent && value !== oldValue) {
            const changeEvent = new CustomEvent('rsl-star-change', {
                bubbles: true,
                detail: { value, oldValue, element }
            });
            element.dispatchEvent(changeEvent);

            // Publish to FilterBus if configured
            if (config.filterField && window.RSL && window.RSL.Filter && window.RSL.Filter.publish) {
                window.RSL.Filter.publish(config.filterField, value);
            }
        }
    }

    /**
     * Get the current rating value
     * @param {HTMLElement} element - The star rating element
     * @returns {number} Current value
     */
    function getValue(element) {
        return parseFloat(element.dataset.starValue) || 0;
    }

    /**
     * Clear the rating
     * @param {HTMLElement} element - The star rating element
     */
    function clear(element) {
        const oldValue = getValue(element);

        if (oldValue > 0) {
            setValue(element, 0, true);

            const clearEvent = new CustomEvent('rsl-star-clear', {
                bubbles: true,
                detail: { oldValue, element }
            });
            element.dispatchEvent(clearEvent);

            announce(element, 'Rating cleared');
        }
    }

    /**
     * Attach event listeners
     * @param {HTMLElement} element - The star rating element
     * @param {Object} config - Configuration object
     */
    function attachEventListeners(element, config) {
        if (config.readonly) {
            return;
        }

        // Mouse events
        const starsContainer = element.querySelector('.rsl-star-rating-stars');

        starsContainer.addEventListener('mousemove', function(e) {
            const hoverValue = calculateValueFromMouse(element, e, config);
            showHoverPreview(element, hoverValue, config);
        });

        starsContainer.addEventListener('mouseleave', function() {
            clearHoverPreview(element);
        });

        starsContainer.addEventListener('click', function(e) {
            const clickValue = calculateValueFromMouse(element, e, config);
            setValue(element, clickValue);
            element.focus();
        });

        // Keyboard events
        element.addEventListener('keydown', function(e) {
            const currentValue = getValue(element);
            const step = config.half ? 0.5 : 1;

            switch (e.key) {
                case 'ArrowRight':
                case 'ArrowUp':
                    e.preventDefault();
                    setValue(element, Math.min(config.max, currentValue + step));
                    break;

                case 'ArrowLeft':
                case 'ArrowDown':
                    e.preventDefault();
                    setValue(element, Math.max(0, currentValue - step));
                    break;

                case 'Home':
                    e.preventDefault();
                    setValue(element, 0);
                    break;

                case 'End':
                    e.preventDefault();
                    setValue(element, config.max);
                    break;

                case 'Delete':
                case 'Backspace':
                    if (config.clearable) {
                        e.preventDefault();
                        clear(element);
                    }
                    break;
            }
        });

        // Clear button
        const clearBtn = element.querySelector('.rsl-star-rating-clear');
        if (clearBtn) {
            clearBtn.addEventListener('click', function(e) {
                e.stopPropagation();
                clear(element);
                element.focus();
            });
        }

        // Touch events for mobile
        let touchStartValue = null;

        starsContainer.addEventListener('touchstart', function(e) {
            touchStartValue = calculateValueFromMouse(element, e.touches[0], config);
            showHoverPreview(element, touchStartValue, config);
        }, { passive: true });

        starsContainer.addEventListener('touchmove', function(e) {
            const touchValue = calculateValueFromMouse(element, e.touches[0], config);
            showHoverPreview(element, touchValue, config);
            touchStartValue = touchValue;
        }, { passive: true });

        starsContainer.addEventListener('touchend', function(e) {
            if (touchStartValue !== null) {
                setValue(element, touchStartValue);
                clearHoverPreview(element);
                touchStartValue = null;
            }
        });
    }

    /**
     * Initialize a single star rating instance
     * @param {HTMLElement} element - The element to initialize
     */
    function create(element) {
        if (!element || instances.has(element)) {
            return;
        }

        const config = getConfig(element);

        // Add base class
        element.classList.add('rsl-star-rating');

        // Build the UI
        buildStarRating(element, config);

        // Attach events
        attachEventListeners(element, config);

        // Mark as initialized
        instances.add(element);

        return element;
    }

    /**
     * Initialize all star rating components on the page
     */
    function init() {
        const elements = document.querySelectorAll('[data-rsl-star-rating]');
        elements.forEach(function(element) {
            create(element);
        });
    }

    /**
     * Destroy a star rating instance
     * @param {HTMLElement} element - The element to destroy
     */
    function destroy(element) {
        if (!element || !instances.has(element)) {
            return;
        }

        // Remove from instances
        instances.delete(element);

        // Clear content
        element.innerHTML = '';

        // Remove classes
        element.classList.remove('rsl-star-rating', 'rsl-star-rating-required', 'rsl-star-rating-error');

        // Remove ARIA attributes
        element.removeAttribute('role');
        element.removeAttribute('aria-valuemin');
        element.removeAttribute('aria-valuemax');
        element.removeAttribute('aria-valuenow');
        element.removeAttribute('aria-valuetext');
        element.removeAttribute('aria-labelledby');
        element.removeAttribute('tabindex');

        // Remove custom properties
        element.style.removeProperty('--star-color');
        element.style.removeProperty('--star-empty-color');
    }

    /**
     * Create a filter mode star rating
     * @param {HTMLElement} container - Container element
     * @param {Object} options - Filter options
     */
    function createFilter(container, options) {
        options = Object.assign({
            max: 5,
            showCounts: false,
            counts: {},
            field: 'rating',
            inline: false,
            upAndAbove: true
        }, options);

        container.classList.add('rsl-star-rating-filter');
        if (options.inline) {
            container.classList.add('rsl-star-rating-filter-inline');
        }

        container.setAttribute('role', 'radiogroup');
        container.setAttribute('aria-label', 'Filter by rating');

        // Create "All ratings" option
        const allOption = createFilterOption(container, 0, options, 'All ratings');
        allOption.querySelector('input').checked = true;
        allOption.classList.add('rsl-star-rating-filter-active');

        // Create rating options
        for (let i = options.max; i >= 1; i--) {
            const label = options.upAndAbove && i < options.max ? `${i}+ stars` : `${i} stars`;
            createFilterOption(container, i, options, label);
        }

        // Handle selection
        container.addEventListener('change', function(e) {
            const selectedValue = parseInt(e.target.value, 10);

            // Update active state
            container.querySelectorAll('.rsl-star-rating-filter-option').forEach(opt => {
                opt.classList.remove('rsl-star-rating-filter-active');
            });
            e.target.closest('.rsl-star-rating-filter-option').classList.add('rsl-star-rating-filter-active');

            // Publish to FilterBus
            if (window.RSL && window.RSL.FilterBus && window.RSL.FilterBus.publish) {
                window.RSL.FilterBus.publish(options.field, selectedValue);
            }

            // Fire event
            const filterEvent = new CustomEvent('rsl-star-filter-change', {
                bubbles: true,
                detail: { value: selectedValue, field: options.field }
            });
            container.dispatchEvent(filterEvent);
        });

        return container;
    }

    /**
     * Create a single filter option
     * @param {HTMLElement} container - Container element
     * @param {number} value - Rating value
     * @param {Object} options - Filter options
     * @param {string} labelText - Label text
     * @returns {HTMLElement} The filter option element
     */
    function createFilterOption(container, value, options, labelText) {
        const option = document.createElement('label');
        option.className = 'rsl-star-rating-filter-option';

        const input = document.createElement('input');
        input.type = 'radio';
        input.name = `star-filter-${options.field}`;
        input.value = value;
        option.appendChild(input);

        if (value > 0) {
            // Create mini star display
            const starsDisplay = document.createElement('span');
            starsDisplay.className = 'rsl-star-rating-stars';
            starsDisplay.setAttribute('aria-hidden', 'true');

            for (let i = 1; i <= options.max; i++) {
                const star = document.createElement('span');
                star.className = 'rsl-star' + (i <= value ? ' rsl-star-filled' : '');
                star.innerHTML = ICONS.star;
                star.style.width = '14px';
                star.style.height = '14px';
                starsDisplay.appendChild(star);
            }

            option.appendChild(starsDisplay);
        }

        const label = document.createElement('span');
        label.className = 'rsl-star-rating-filter-label';
        label.textContent = labelText;
        option.appendChild(label);

        if (options.showCounts && options.counts[value] !== undefined) {
            const count = document.createElement('span');
            count.className = 'rsl-star-rating-filter-count';
            count.textContent = `(${options.counts[value]})`;
            option.appendChild(count);
        }

        container.appendChild(option);
        return option;
    }

    // Public API
    window.RSL.StarRating = {
        init: init,
        create: create,
        destroy: destroy,
        getValue: getValue,
        setValue: function(element, value) {
            setValue(element, value, false);
        },
        clear: clear,
        createFilter: createFilter
    };

    // Auto-initialize on DOMContentLoaded
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();
