/**
 * RSL Animate on Scroll Component
 * Version: 1.0.0
 *
 * Provides scroll-triggered animations for any element using data attributes.
 * Uses IntersectionObserver for performant scroll detection and respects
 * user's reduced-motion preferences for accessibility.
 *
 * @fileoverview Animate on Scroll component for RSL
 * @license MIT
 *
 * Usage:
 * <div data-animate="fade-in" data-animate-delay="150" data-animate-duration="600" data-animate-once="true">
 *   Content here
 * </div>
 *
 * Supported Animations:
 * - fade-in, fade-from-top, fade-from-bottom, fade-from-left, fade-from-right
 * - fade-zoom-in, slide-in-right, fade-slide-up, fade-slide-down
 * - spin-in, expand-open, blink-in
 *
 * Data Attributes:
 * - data-animate: Animation name (required)
 * - data-animate-delay: Delay before animation starts in ms (optional)
 * - data-animate-duration: Duration of animation in ms (optional)
 * - data-animate-once: "true" to animate only once, "false" to repeat (optional, default: false)
 *
 * Public API:
 * - RSL.AnimateOnScroll.init() - Initialize all elements with data-animate attribute
 * - RSL.AnimateOnScroll.observe(element) - Observe a specific element
 * - RSL.AnimateOnScroll.unobserve(element) - Stop observing an element
 * - RSL.AnimateOnScroll.reset() - Reset all animations
 */

(function() {
    'use strict';

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

    /**
     * Track observed elements to prevent double initialization
     * @type {WeakSet}
     */
    const observedElements = new WeakSet();

    /**
     * IntersectionObserver instance
     * @type {IntersectionObserver|null}
     */
    let observer = null;

    /**
     * Check if user prefers reduced motion
     * @returns {boolean} True if reduced motion is preferred
     */
    function prefersReducedMotion() {
        return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    }

    /**
     * Make element immediately visible (for reduced motion or errors)
     * @param {HTMLElement} element - Element to make visible
     */
    function makeVisible(element) {
        element.classList.remove('rsl-animate-pending');
        element.style.opacity = '1';
        element.style.transform = 'none';
    }

    /**
     * Apply animation to element
     * @param {HTMLElement} element - Element to animate
     */
    function applyAnimation(element) {
        const animationName = element.getAttribute('data-animate');
        const delay = element.getAttribute('data-animate-delay');
        const duration = element.getAttribute('data-animate-duration');

        if (!animationName) {
            console.warn('RSL Animate on Scroll: No animation name specified', element);
            makeVisible(element);
            return;
        }

        // Remove pending class
        element.classList.remove('rsl-animate-pending');

        // Apply animation class
        element.classList.add(animationName);

        // Apply custom delay if specified
        if (delay) {
            element.style.animationDelay = delay + 'ms';
        }

        // Apply custom duration if specified
        if (duration) {
            element.style.animationDuration = duration + 'ms';
        }
    }

    /**
     * Reset element animation
     * @param {HTMLElement} element - Element to reset
     */
    function resetAnimation(element) {
        const animationName = element.getAttribute('data-animate');

        if (!animationName) {
            return;
        }

        // Remove animation class
        element.classList.remove(animationName);

        // Add pending class back
        element.classList.add('rsl-animate-pending');

        // Reset inline styles
        element.style.animationDelay = '';
        element.style.animationDuration = '';
    }

    /**
     * Intersection Observer callback
     * @param {IntersectionObserverEntry[]} entries - Observed entries
     */
    function handleIntersection(entries) {
        entries.forEach(function(entry) {
            const element = entry.target;
            const animateOnce = element.getAttribute('data-animate-once') === 'true';

            if (entry.isIntersecting) {
                // Element entered viewport - animate it
                applyAnimation(element);

                // If animate-once is true, stop observing this element
                if (animateOnce && observer) {
                    observer.unobserve(element);
                }
            } else {
                // Element left viewport
                if (!animateOnce) {
                    // If not animate-once, reset the animation for next time
                    resetAnimation(element);
                }
            }
        });
    }

    /**
     * Create and configure IntersectionObserver
     * @returns {IntersectionObserver} Configured observer
     */
    function createObserver() {
        const options = {
            root: null, // Use viewport as root
            rootMargin: '50px', // Start animation slightly before element enters viewport
            threshold: 0.1 // Trigger when 10% of element is visible
        };

        return new IntersectionObserver(handleIntersection, options);
    }

    /**
     * Initialize animate on scroll for a specific element
     * @param {HTMLElement} element - Element to observe
     */
    function observeElement(element) {
        // Skip if already observed
        if (observedElements.has(element)) {
            return;
        }

        // If user prefers reduced motion, make element visible immediately
        if (prefersReducedMotion()) {
            makeVisible(element);
            return;
        }

        // Add pending class to hide element initially
        element.classList.add('rsl-animate-pending');

        // Create observer if it doesn't exist
        if (!observer) {
            observer = createObserver();
        }

        // Start observing
        observer.observe(element);
        observedElements.add(element);
    }

    /**
     * Stop observing an element
     * @param {HTMLElement} element - Element to unobserve
     */
    function unobserveElement(element) {
        if (observer && observedElements.has(element)) {
            observer.unobserve(element);
        }
    }

    /**
     * Initialize all elements with data-animate attribute
     * This is the main init method that scans the entire page
     */
    function init() {
        // Find all elements with data-animate attribute
        const elements = document.querySelectorAll('[data-animate]');

        elements.forEach(function(element) {
            observeElement(element);
        });
    }

    /**
     * Reset all animations
     * Useful for testing or restarting animations
     */
    function reset() {
        const elements = document.querySelectorAll('[data-animate]');

        elements.forEach(function(element) {
            resetAnimation(element);

            // Re-observe if observer exists
            if (observer && observedElements.has(element)) {
                observer.unobserve(element);
                observer.observe(element);
            }
        });
    }

    /**
     * Public API
     */
    window.RSL.AnimateOnScroll = {
        /**
         * Initialize all elements with data-animate attribute
         * @public
         * @example
         * RSL.AnimateOnScroll.init();
         */
        init: init,

        /**
         * Observe a specific element
         * @public
         * @param {HTMLElement} element - Element to observe
         * @example
         * const element = document.querySelector('.my-element');
         * RSL.AnimateOnScroll.observe(element);
         */
        observe: observeElement,

        /**
         * Stop observing an element
         * @public
         * @param {HTMLElement} element - Element to unobserve
         * @example
         * const element = document.querySelector('.my-element');
         * RSL.AnimateOnScroll.unobserve(element);
         */
        unobserve: unobserveElement,

        /**
         * Reset all animations
         * @public
         * @example
         * RSL.AnimateOnScroll.reset();
         */
        reset: reset
    };

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

    /**
     * Observe DOM for dynamically added elements with data-animate
     */
    if (window.MutationObserver) {
        const mutationObserver = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                mutation.addedNodes.forEach(function(node) {
                    // Only process element nodes
                    if (node.nodeType === 1) {
                        // Check if the added node itself has data-animate
                        if (node.hasAttribute && node.hasAttribute('data-animate')) {
                            observeElement(node);
                        }
                        // Check for data-animate in descendants
                        if (node.querySelectorAll) {
                            const descendants = node.querySelectorAll('[data-animate]');
                            descendants.forEach(function(element) {
                                observeElement(element);
                            });
                        }
                    }
                });
            });
        });

        mutationObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

})();
