/* ============================================
   RSL Timeline Component
   Version: 1.1.0
   Description: Powerful, flexible timeline component
   for displaying chronological events, milestones,
   and progress with full accessibility support.
   FilterBus: Visibility mode for events
   ============================================ */

(function() {
    'use strict';

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

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

    // Track FilterBus subscriptions for cleanup
    var filterSubscriptions = new Map();

    /**
     * Timeline Component
     * Full-featured chronological event display
     */
    window.RSL.Timeline = {

        /**
         * Initialize all timelines on the page
         */
        init: function() {
            var timelines = document.querySelectorAll('[data-rsl-timeline]');
            timelines.forEach(function(el) {
                if (!instances.has(el)) {
                    RSL.Timeline.create(el);
                }
            });
        },

        /**
         * Create/initialize a single timeline
         * @param {HTMLElement} element - The timeline container
         * @param {Object} options - Optional configuration override
         * @returns {HTMLElement} The initialized element
         */
        create: function(element, options) {
            if (!element) return null;
            if (instances.has(element)) return element;

            // Parse configuration from data attributes
            var config = parseConfig(element, options);

            // Store config on element
            element._rslTimeline = config;

            // Build timeline structure
            buildTimeline(element, config);

            // Setup accessibility
            setupAccessibility(element, config);

            // Setup keyboard navigation
            setupKeyboardNav(element, config);

            // Setup collapsible functionality
            if (config.collapsible) {
                setupCollapsible(element, config);
            }

            // Setup animate on scroll
            if (config.animate) {
                setupAnimateOnScroll(element, config);
            }

            // Setup FilterBus integration
            if (config.filterSubscribe) {
                setupFilterBus(element, config);
            }

            // Track instance
            instances.add(element);

            // Dispatch init event
            element.dispatchEvent(new CustomEvent('rsl-timeline-init', {
                bubbles: true,
                detail: { config: config }
            }));

            return element;
        },

        /**
         * Add an event to the timeline
         * @param {HTMLElement} element - Timeline element
         * @param {Object} eventData - Event data object
         * @param {number} index - Optional position index
         * @returns {HTMLElement} The created event element
         */
        addEvent: function(element, eventData, index) {
            if (!element || !element._rslTimeline) return null;

            var config = element._rslTimeline;
            var eventEl = createEventElement(eventData, config);

            // Determine position
            var events = element.querySelectorAll('.rsl-timeline-event');
            if (typeof index === 'number' && index >= 0 && index < events.length) {
                events[index].before(eventEl);
            } else {
                // Add to end (before any group headers if present)
                var lastGroup = element.querySelector('.rsl-timeline-group:last-child');
                if (lastGroup) {
                    lastGroup.appendChild(eventEl);
                } else {
                    element.appendChild(eventEl);
                }
            }

            // Setup event-specific functionality
            if (config.collapsible) {
                setupEventCollapsible(eventEl);
            }

            if (config.animate) {
                // Trigger animation
                setTimeout(function() {
                    eventEl.classList.add('rsl-visible');
                }, 50);
            }

            // Update accessibility
            updateAriaPositions(element);

            return eventEl;
        },

        /**
         * Remove an event from the timeline
         * @param {HTMLElement} element - Timeline element
         * @param {string} id - Event ID to remove
         */
        removeEvent: function(element, id) {
            if (!element) return;

            var eventEl = element.querySelector('[data-event-id="' + id + '"]');
            if (eventEl) {
                eventEl.remove();
                updateAriaPositions(element);
            }
        },

        /**
         * Update an event's data
         * @param {HTMLElement} element - Timeline element
         * @param {string} id - Event ID to update
         * @param {Object} data - Updated data
         */
        updateEvent: function(element, id, data) {
            if (!element || !element._rslTimeline) return;

            var eventEl = element.querySelector('[data-event-id="' + id + '"]');
            if (!eventEl) return;

            // Update content elements
            if (data.title !== undefined) {
                var titleEl = eventEl.querySelector('.rsl-timeline-title');
                if (titleEl) {
                    // Preserve the expand icon if collapsible
                    var text = titleEl.childNodes[0];
                    if (text && text.nodeType === Node.TEXT_NODE) {
                        text.textContent = data.title;
                    } else {
                        titleEl.textContent = data.title;
                    }
                }
            }

            if (data.description !== undefined) {
                var descEl = eventEl.querySelector('.rsl-timeline-description');
                if (descEl) {
                    descEl.textContent = data.description;
                }
            }

            if (data.date !== undefined) {
                var dateEl = eventEl.querySelector('.rsl-timeline-date');
                if (dateEl) {
                    dateEl.textContent = data.date;
                    if (data.datetime) {
                        dateEl.setAttribute('datetime', data.datetime);
                    }
                }
            }

            if (data.status !== undefined) {
                eventEl.setAttribute('data-status', data.status);
                updateMarkerIcon(eventEl, data.status);
            }

            if (data.type !== undefined) {
                eventEl.setAttribute('data-type', data.type);
            }
        },

        /**
         * Filter timeline events
         * @param {HTMLElement} element - Timeline element
         * @param {Object} criteria - Filter criteria
         */
        filter: function(element, criteria) {
            if (!element) return;

            var events = element.querySelectorAll('.rsl-timeline-event');
            var visibleCount = 0;

            events.forEach(function(eventEl) {
                var show = true;

                // Filter by status
                if (criteria.status && criteria.status !== 'all') {
                    show = show && eventEl.getAttribute('data-status') === criteria.status;
                }

                // Filter by type
                if (criteria.type && criteria.type !== 'all') {
                    show = show && eventEl.getAttribute('data-type') === criteria.type;
                }

                // Filter by category
                if (criteria.category && criteria.category !== 'all') {
                    show = show && eventEl.getAttribute('data-category') === criteria.category;
                }

                // Filter by search text
                if (criteria.search) {
                    var searchText = criteria.search.toLowerCase();
                    var title = (eventEl.querySelector('.rsl-timeline-title')?.textContent || '').toLowerCase();
                    var desc = (eventEl.querySelector('.rsl-timeline-description')?.textContent || '').toLowerCase();
                    show = show && (title.includes(searchText) || desc.includes(searchText));
                }

                // Apply filter
                if (show) {
                    eventEl.classList.remove('rsl-filtered-out');
                    visibleCount++;
                } else {
                    eventEl.classList.add('rsl-filtered-out');
                }
            });

            // Dispatch filter event
            element.dispatchEvent(new CustomEvent('rsl-timeline-filter', {
                bubbles: true,
                detail: { criteria: criteria, visibleCount: visibleCount }
            }));
        },

        /**
         * Clear all filters
         * @param {HTMLElement} element - Timeline element
         */
        clearFilter: function(element) {
            if (!element) return;

            var events = element.querySelectorAll('.rsl-timeline-event');
            events.forEach(function(eventEl) {
                eventEl.classList.remove('rsl-filtered-out');
            });
        },

        /**
         * Expand a collapsible event
         * @param {HTMLElement} element - Timeline element
         * @param {string} id - Event ID to expand
         */
        expand: function(element, id) {
            if (!element) return;

            var eventEl = element.querySelector('[data-event-id="' + id + '"]');
            if (eventEl) {
                expandEvent(eventEl);
            }
        },

        /**
         * Collapse a collapsible event
         * @param {HTMLElement} element - Timeline element
         * @param {string} id - Event ID to collapse
         */
        collapse: function(element, id) {
            if (!element) return;

            var eventEl = element.querySelector('[data-event-id="' + id + '"]');
            if (eventEl) {
                collapseEvent(eventEl);
            }
        },

        /**
         * Expand all events
         * @param {HTMLElement} element - Timeline element
         */
        expandAll: function(element) {
            if (!element) return;

            var events = element.querySelectorAll('.rsl-timeline-event');
            events.forEach(function(eventEl) {
                expandEvent(eventEl);
            });
        },

        /**
         * Collapse all events
         * @param {HTMLElement} element - Timeline element
         */
        collapseAll: function(element) {
            if (!element) return;

            var events = element.querySelectorAll('.rsl-timeline-event');
            events.forEach(function(eventEl) {
                collapseEvent(eventEl);
            });
        },

        /**
         * Scroll to a specific event
         * @param {HTMLElement} element - Timeline element
         * @param {string} id - Event ID to scroll to
         */
        scrollTo: function(element, id) {
            if (!element) return;

            var eventEl = element.querySelector('[data-event-id="' + id + '"]');
            if (eventEl) {
                eventEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
                eventEl.focus();
            }
        },

        /**
         * Get the current/active event
         * @param {HTMLElement} element - Timeline element
         * @returns {HTMLElement|null} The current event element
         */
        getCurrent: function(element) {
            if (!element) return null;
            return element.querySelector('.rsl-timeline-event[data-status="current"]');
        },

        /**
         * Get all events
         * @param {HTMLElement} element - Timeline element
         * @returns {Array} Array of event elements
         */
        getEvents: function(element) {
            if (!element) return [];
            return Array.from(element.querySelectorAll('.rsl-timeline-event'));
        },

        /**
         * Get events as JSON data
         * @param {HTMLElement} element - Timeline element
         * @returns {Array} Array of event data objects
         */
        getEventsData: function(element) {
            if (!element) return [];

            var events = element.querySelectorAll('.rsl-timeline-event');
            return Array.from(events).map(function(eventEl) {
                return {
                    id: eventEl.getAttribute('data-event-id'),
                    title: eventEl.querySelector('.rsl-timeline-title')?.textContent || '',
                    description: eventEl.querySelector('.rsl-timeline-description')?.textContent || '',
                    date: eventEl.querySelector('.rsl-timeline-date')?.textContent || '',
                    status: eventEl.getAttribute('data-status'),
                    type: eventEl.getAttribute('data-type'),
                    category: eventEl.getAttribute('data-category')
                };
            });
        },

        /**
         * Set events from JSON data
         * @param {HTMLElement} element - Timeline element
         * @param {Array} events - Array of event data objects
         */
        setEvents: function(element, events) {
            if (!element || !element._rslTimeline) return;

            var config = element._rslTimeline;

            // Clear existing events
            element.innerHTML = '';

            // Rebuild with new events
            config.events = events;
            buildTimelineEvents(element, config);

            // Re-setup functionality
            setupAccessibility(element, config);

            if (config.collapsible) {
                setupCollapsible(element, config);
            }

            if (config.animate) {
                setupAnimateOnScroll(element, config);
            }
        },

        /**
         * Destroy timeline instance
         * @param {HTMLElement} element - Timeline element
         */
        destroy: function(element) {
            if (!element) return;

            // Remove event listeners
            var events = element.querySelectorAll('.rsl-timeline-event');
            events.forEach(function(eventEl) {
                eventEl.removeEventListener('click', eventEl._clickHandler);
                eventEl.removeEventListener('keydown', eventEl._keyHandler);
            });

            // Remove observer
            if (element._animateObserver) {
                element._animateObserver.disconnect();
            }

            // Remove FilterBus subscription (new API)
            var subscriberId = filterSubscriptions.get(element);
            if (subscriberId && window.RSL && window.RSL.FilterBus && window.RSL.FilterBus.unsubscribe) {
                window.RSL.FilterBus.unsubscribe(subscriberId);
            }
            filterSubscriptions.delete(element);

            // Remove FilterBus subscription (legacy API)
            if (element._filterUnsubscribe) {
                element._filterUnsubscribe();
            }

            // Clear config
            delete element._rslTimeline;

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

    /**
     * Parse configuration from data attributes
     * @private
     */
    function parseConfig(element, options) {
        options = options || {};

        // Parse events JSON if provided
        var events = [];
        var eventsAttr = element.getAttribute('data-events');
        if (eventsAttr) {
            try {
                events = JSON.parse(eventsAttr);
            } catch (e) {
                console.warn('RSL Timeline: Invalid events JSON');
            }
        }

        return {
            orientation: element.getAttribute('data-timeline-orientation') || options.orientation || 'vertical',
            variant: element.getAttribute('data-timeline-variant') || options.variant || 'default',
            size: element.getAttribute('data-timeline-size') || options.size || 'md',
            connector: element.getAttribute('data-timeline-connector') || options.connector || 'solid',
            alternate: element.hasAttribute('data-timeline-alternate') || options.alternate || false,
            animate: element.hasAttribute('data-timeline-animate') || options.animate || false,
            collapsible: element.hasAttribute('data-timeline-collapsible') || options.collapsible || false,
            clickable: element.hasAttribute('data-timeline-clickable') || options.clickable || false,
            groupBy: element.getAttribute('data-timeline-group-by') || options.groupBy || null,
            // FilterBus integration
            filterSubscribe: element.getAttribute('data-filter-subscribe') || element.getAttribute('data-filterbus-subscribe') || options.filterSubscribe || null,
            filterField: element.getAttribute('data-filter-field') || options.filterField || 'status',
            events: events.length > 0 ? events : (options.events || [])
        };
    }

    /**
     * Build timeline structure
     * @private
     */
    function buildTimeline(element, config) {
        // Add base class
        element.classList.add('rsl-timeline');

        // Add orientation class
        element.classList.add('rsl-timeline-' + config.orientation);

        // Add variant class
        if (config.variant !== 'default') {
            element.classList.add('rsl-timeline-' + config.variant);
        }

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

        // Add connector style
        if (config.connector !== 'solid') {
            element.classList.add('rsl-timeline-connector-' + config.connector);
        }

        // Add alternate/split class
        if (config.alternate && config.orientation === 'vertical') {
            element.classList.add('rsl-timeline-split');
        }

        // Add animate class
        if (config.animate) {
            element.classList.add('rsl-timeline-animate');
        }

        // Add collapsible class
        if (config.collapsible) {
            element.classList.add('rsl-timeline-collapsible');
        }

        // Add clickable class
        if (config.clickable) {
            element.classList.add('rsl-timeline-clickable');
        }

        // Build events if not already in HTML
        if (config.events.length > 0 && element.children.length === 0) {
            buildTimelineEvents(element, config);
        } else {
            // Process existing HTML events
            processExistingEvents(element, config);
        }
    }

    /**
     * Build timeline events from JSON data
     * @private
     */
    function buildTimelineEvents(element, config) {
        // Group events if needed
        if (config.groupBy) {
            var groups = groupEvents(config.events, config.groupBy);
            Object.keys(groups).forEach(function(groupName) {
                var groupEl = document.createElement('div');
                groupEl.className = 'rsl-timeline-group';

                var headerEl = document.createElement('div');
                headerEl.className = 'rsl-timeline-group-header';
                headerEl.textContent = groupName;
                groupEl.appendChild(headerEl);

                groups[groupName].forEach(function(eventData) {
                    var eventEl = createEventElement(eventData, config);
                    groupEl.appendChild(eventEl);
                });

                element.appendChild(groupEl);
            });
        } else {
            config.events.forEach(function(eventData) {
                var eventEl = createEventElement(eventData, config);
                element.appendChild(eventEl);
            });
        }
    }

    /**
     * Process existing HTML events
     * @private
     */
    function processExistingEvents(element, config) {
        var events = element.querySelectorAll('.rsl-timeline-event');
        events.forEach(function(eventEl, index) {
            // Ensure event has ID
            if (!eventEl.getAttribute('data-event-id')) {
                eventEl.setAttribute('data-event-id', 'event-' + (index + 1));
            }

            // Add marker if not present
            if (!eventEl.querySelector('.rsl-timeline-marker')) {
                var marker = document.createElement('div');
                marker.className = 'rsl-timeline-marker';

                // Add icon based on status
                var status = eventEl.getAttribute('data-status') || 'upcoming';
                updateMarkerIcon(eventEl, status, marker);

                eventEl.insertBefore(marker, eventEl.firstChild);
            }

            // Wrap content if not wrapped
            var content = eventEl.querySelector('.rsl-timeline-content');
            if (!content) {
                content = document.createElement('div');
                content.className = 'rsl-timeline-content';

                // Move all non-marker children to content
                var children = Array.from(eventEl.children).filter(function(child) {
                    return !child.classList.contains('rsl-timeline-marker');
                });

                children.forEach(function(child) {
                    content.appendChild(child);
                });

                eventEl.appendChild(content);
            }
        });
    }

    /**
     * Create a single event element
     * @private
     */
    function createEventElement(data, config) {
        var eventEl = document.createElement('li');
        eventEl.className = 'rsl-timeline-event';
        eventEl.setAttribute('data-event-id', data.id || 'event-' + Date.now());
        eventEl.setAttribute('data-status', data.status || 'upcoming');

        if (data.type) {
            eventEl.setAttribute('data-type', data.type);
        }

        if (data.category) {
            eventEl.setAttribute('data-category', data.category);
        }

        // Marker
        var marker = document.createElement('div');
        marker.className = 'rsl-timeline-marker';
        updateMarkerIcon(eventEl, data.status || 'upcoming', marker);
        eventEl.appendChild(marker);

        // Content wrapper
        var content = document.createElement('div');
        content.className = 'rsl-timeline-content';

        // Date
        if (data.date) {
            var dateEl = document.createElement('time');
            dateEl.className = 'rsl-timeline-date';
            dateEl.textContent = data.date;
            if (data.datetime) {
                dateEl.setAttribute('datetime', data.datetime);
            }
            content.appendChild(dateEl);
        }

        // Title
        var titleEl = document.createElement('h3');
        titleEl.className = 'rsl-timeline-title';
        titleEl.textContent = data.title || '';
        content.appendChild(titleEl);

        // Description and other content (wrapped for collapsible)
        var detailsEl = document.createElement('div');
        detailsEl.className = 'rsl-timeline-details';

        if (data.description) {
            var descEl = document.createElement('p');
            descEl.className = 'rsl-timeline-description';
            descEl.textContent = data.description;
            detailsEl.appendChild(descEl);
        }

        // Media
        if (data.media) {
            var mediaEl = document.createElement('div');
            mediaEl.className = 'rsl-timeline-media';

            if (data.media.type === 'image') {
                var img = document.createElement('img');
                img.src = data.media.src;
                img.alt = data.media.alt || '';
                mediaEl.appendChild(img);
            } else if (data.media.type === 'video') {
                var video = document.createElement('video');
                video.src = data.media.src;
                video.controls = true;
                mediaEl.appendChild(video);
            }

            detailsEl.appendChild(mediaEl);
        }

        // Link
        if (data.link) {
            var linkEl = document.createElement('a');
            linkEl.className = 'rsl-timeline-link';
            linkEl.href = data.link.href;
            linkEl.textContent = data.link.text || 'Learn more';

            var linkIcon = document.createElement('i');
            linkIcon.className = 'fas fa-arrow-right';
            linkEl.appendChild(linkIcon);

            detailsEl.appendChild(linkEl);
        }

        // Meta badges
        if (data.type || data.category) {
            var metaEl = document.createElement('div');
            metaEl.className = 'rsl-timeline-meta';

            if (data.type) {
                var typeBadge = document.createElement('span');
                typeBadge.className = 'rsl-timeline-badge ' + data.type;
                typeBadge.textContent = data.type.charAt(0).toUpperCase() + data.type.slice(1);
                metaEl.appendChild(typeBadge);
            }

            if (data.category) {
                var catBadge = document.createElement('span');
                catBadge.className = 'rsl-timeline-badge';
                catBadge.textContent = data.category;
                metaEl.appendChild(catBadge);
            }

            detailsEl.appendChild(metaEl);
        }

        content.appendChild(detailsEl);
        eventEl.appendChild(content);

        return eventEl;
    }

    /**
     * Update marker icon based on status
     * @private
     */
    function updateMarkerIcon(eventEl, status, marker) {
        marker = marker || eventEl.querySelector('.rsl-timeline-marker');
        if (!marker) return;

        // Clear existing icon
        marker.innerHTML = '';

        var icon = document.createElement('i');

        switch (status) {
            case 'completed':
                icon.className = 'fas fa-check';
                break;
            case 'current':
                icon.className = 'fas fa-circle';
                icon.style.fontSize = '0.375rem';
                break;
            case 'cancelled':
                icon.className = 'fas fa-times';
                break;
            default:
                // No icon for upcoming
                return;
        }

        marker.appendChild(icon);
    }

    /**
     * Group events by specified field
     * @private
     */
    function groupEvents(events, groupBy) {
        var groups = {};

        events.forEach(function(event) {
            var key;

            switch (groupBy) {
                case 'month':
                    if (event.datetime) {
                        var date = new Date(event.datetime);
                        key = date.toLocaleString('default', { month: 'long', year: 'numeric' });
                    } else {
                        key = 'Unknown';
                    }
                    break;
                case 'year':
                    if (event.datetime) {
                        key = new Date(event.datetime).getFullYear().toString();
                    } else {
                        key = 'Unknown';
                    }
                    break;
                case 'category':
                    key = event.category || 'Uncategorized';
                    break;
                case 'status':
                    key = event.status || 'Unknown';
                    break;
                default:
                    key = event.date || 'Unknown';
            }

            if (!groups[key]) {
                groups[key] = [];
            }
            groups[key].push(event);
        });

        return groups;
    }

    /**
     * Setup accessibility attributes
     * @private
     */
    function setupAccessibility(element, config) {
        // Set role
        element.setAttribute('role', 'list');

        // Set aria-label
        if (!element.getAttribute('aria-label')) {
            element.setAttribute('aria-label', 'Timeline');
        }

        // Setup each event
        var events = element.querySelectorAll('.rsl-timeline-event');
        events.forEach(function(eventEl, index) {
            eventEl.setAttribute('role', 'listitem');
            eventEl.setAttribute('tabindex', '0');

            // Set aria-current for current event
            if (eventEl.getAttribute('data-status') === 'current') {
                eventEl.setAttribute('aria-current', 'step');
            }

            // Set aria-setsize and aria-posinset
            eventEl.setAttribute('aria-setsize', events.length);
            eventEl.setAttribute('aria-posinset', index + 1);

            // Add screen reader text for status
            var status = eventEl.getAttribute('data-status') || 'upcoming';
            var srText = eventEl.querySelector('.rsl-timeline-sr-only');
            if (!srText) {
                srText = document.createElement('span');
                srText.className = 'rsl-timeline-sr-only';
                eventEl.insertBefore(srText, eventEl.firstChild);
            }
            srText.textContent = 'Status: ' + status + '. ';
        });
    }

    /**
     * Update ARIA position attributes
     * @private
     */
    function updateAriaPositions(element) {
        var events = element.querySelectorAll('.rsl-timeline-event:not(.rsl-filtered-out)');
        events.forEach(function(eventEl, index) {
            eventEl.setAttribute('aria-setsize', events.length);
            eventEl.setAttribute('aria-posinset', index + 1);
        });
    }

    /**
     * Setup keyboard navigation
     * @private
     */
    function setupKeyboardNav(element, config) {
        var isHorizontal = config.orientation === 'horizontal';

        element.addEventListener('keydown', function(e) {
            var currentEvent = document.activeElement;
            if (!currentEvent || !currentEvent.classList.contains('rsl-timeline-event')) {
                return;
            }

            var events = Array.from(element.querySelectorAll('.rsl-timeline-event:not(.rsl-filtered-out)'));
            var currentIndex = events.indexOf(currentEvent);

            var prevKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';
            var nextKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';

            switch (e.key) {
                case prevKey:
                    e.preventDefault();
                    if (currentIndex > 0) {
                        events[currentIndex - 1].focus();
                    }
                    break;

                case nextKey:
                    e.preventDefault();
                    if (currentIndex < events.length - 1) {
                        events[currentIndex + 1].focus();
                    }
                    break;

                case 'Home':
                    e.preventDefault();
                    events[0].focus();
                    break;

                case 'End':
                    e.preventDefault();
                    events[events.length - 1].focus();
                    break;

                case 'Enter':
                case ' ':
                    if (config.collapsible) {
                        e.preventDefault();
                        toggleEvent(currentEvent);
                    } else if (config.clickable) {
                        e.preventDefault();
                        currentEvent.dispatchEvent(new CustomEvent('rsl-timeline-event-click', {
                            bubbles: true,
                            detail: { id: currentEvent.getAttribute('data-event-id') }
                        }));
                    }
                    break;
            }
        });
    }

    /**
     * Setup collapsible functionality
     * @private
     */
    function setupCollapsible(element, config) {
        var events = element.querySelectorAll('.rsl-timeline-event');

        events.forEach(function(eventEl) {
            setupEventCollapsible(eventEl);
        });
    }

    /**
     * Setup collapsible for a single event
     * @private
     */
    function setupEventCollapsible(eventEl) {
        // Set initial collapsed state
        eventEl.setAttribute('aria-expanded', 'false');

        var title = eventEl.querySelector('.rsl-timeline-title');
        if (title) {
            title.addEventListener('click', function(e) {
                e.stopPropagation();
                toggleEvent(eventEl);
            });
        }
    }

    /**
     * Toggle event expand/collapse
     * @private
     */
    function toggleEvent(eventEl) {
        var isExpanded = eventEl.getAttribute('aria-expanded') === 'true';

        if (isExpanded) {
            collapseEvent(eventEl);
        } else {
            expandEvent(eventEl);
        }
    }

    /**
     * Expand an event
     * @private
     */
    function expandEvent(eventEl) {
        eventEl.setAttribute('aria-expanded', 'true');

        eventEl.dispatchEvent(new CustomEvent('rsl-timeline-event-expand', {
            bubbles: true,
            detail: { id: eventEl.getAttribute('data-event-id') }
        }));
    }

    /**
     * Collapse an event
     * @private
     */
    function collapseEvent(eventEl) {
        eventEl.setAttribute('aria-expanded', 'false');

        eventEl.dispatchEvent(new CustomEvent('rsl-timeline-event-collapse', {
            bubbles: true,
            detail: { id: eventEl.getAttribute('data-event-id') }
        }));
    }

    /**
     * Setup animate on scroll
     * @private
     */
    function setupAnimateOnScroll(element, config) {
        // Check for reduced motion preference
        if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
            // Make all events visible immediately
            var events = element.querySelectorAll('.rsl-timeline-event');
            events.forEach(function(eventEl) {
                eventEl.classList.add('rsl-visible');
            });
            return;
        }

        var observer = new IntersectionObserver(function(entries) {
            entries.forEach(function(entry) {
                if (entry.isIntersecting) {
                    entry.target.classList.add('rsl-visible');
                }
            });
        }, {
            threshold: 0.2,
            rootMargin: '0px 0px -50px 0px'
        });

        var events = element.querySelectorAll('.rsl-timeline-event');
        events.forEach(function(eventEl) {
            observer.observe(eventEl);
        });

        // Store observer for cleanup
        element._animateObserver = observer;
    }

    /**
     * Setup FilterBus integration
     * @private
     */
    function setupFilterBus(element, config) {
        // Check if FilterBus is available
        if (!window.RSL || !window.RSL.FilterBus) {
            // Fallback to older RSL.Filter API for backwards compatibility
            if (window.RSL && window.RSL.Filter && window.RSL.Filter.subscribe) {
                var unsubscribe = RSL.Filter.subscribe(config.filterSubscribe, function(value) {
                    handleFilterBusChange(element, config, value);
                });
                element._filterUnsubscribe = unsubscribe;
            }
            return;
        }

        // Generate unique subscriber ID
        var subscriberId = 'timeline-' + (element.id || Math.random().toString(36).substr(2, 9));

        // Parse subscription keys - support comma-separated keys (e.g., "status,search")
        var subscribeKeys = config.filterSubscribe.split(',').map(function(k) {
            return k.trim();
        });

        // Subscribe to FilterBus
        window.RSL.FilterBus.subscribe(
            subscriberId,
            function(state, meta) {
                handleFilterBusStateChange(element, config, state, meta, subscribeKeys);
            },
            { keys: subscribeKeys }
        );

        // Store subscription ID for cleanup
        filterSubscriptions.set(element, subscriberId);

        // Apply any existing FilterBus state on init
        var currentState = window.RSL.FilterBus.getState();
        if (Object.keys(currentState).length > 0) {
            handleFilterBusStateChange(element, config, currentState, { source: 'init' }, subscribeKeys);
        }

        // Add initialized class after brief delay (prevents transition flicker)
        setTimeout(function() {
            element.classList.add('rsl-timeline-filterbus-initialized');
        }, 50);
    }

    /**
     * Handle FilterBus state changes (new API)
     * @private
     */
    function handleFilterBusStateChange(element, config, state, meta, subscribeKeys) {
        if (!state) return;

        var events = element.querySelectorAll('.rsl-timeline-event');
        var visibleCount = 0;

        events.forEach(function(eventEl) {
            var shouldShow = true;

            // Check each subscription key
            for (var i = 0; i < subscribeKeys.length; i++) {
                var filterKey = subscribeKeys[i];
                var filterValue = state[filterKey];

                // Skip if no filter value set
                if (filterValue === undefined || filterValue === null || filterValue === '') {
                    continue;
                }

                // Check if filter is "All" (case-insensitive) - show everything
                var filterLower = String(filterValue).toLowerCase();
                if (filterLower === 'all') {
                    continue;
                }

                // Handle search-style filters (substring matching)
                if (filterKey === 'search') {
                    var title = (eventEl.querySelector('.rsl-timeline-title')?.textContent || '').toLowerCase();
                    var desc = (eventEl.querySelector('.rsl-timeline-description')?.textContent || '').toLowerCase();
                    var searchText = title + ' ' + desc;
                    if (!searchText.includes(filterLower)) {
                        shouldShow = false;
                        break;
                    }
                    continue;
                }

                // Get the event's value for this filter field
                // Use config.filterField (defaults to 'status') to determine which event attribute to check
                var eventValue = getEventFilterValue(eventEl, config.filterField);

                if (eventValue !== null) {
                    // Support comma-separated values for multi-match
                    var eventValues = String(eventValue).split(',').map(function(v) {
                        return v.trim().toLowerCase();
                    });
                    if (!eventValues.includes(filterLower)) {
                        shouldShow = false;
                        break;
                    }
                }
            }

            // Apply visibility
            if (shouldShow) {
                eventEl.classList.remove('rsl-timeline-event-hidden');
                eventEl.removeAttribute('aria-hidden');
                visibleCount++;
            } else {
                eventEl.classList.add('rsl-timeline-event-hidden');
                eventEl.setAttribute('aria-hidden', 'true');
            }
        });

        // Update ARIA positions for visible events
        updateAriaPositions(element);

        // Dispatch event
        element.dispatchEvent(new CustomEvent('rsl-timeline:filterbus-update', {
            bubbles: true,
            detail: { state: state, meta: meta, visibleCount: visibleCount }
        }));
    }

    /**
     * Handle FilterBus change (legacy/simple API)
     * @private
     */
    function handleFilterBusChange(element, config, value) {
        if (!value || String(value).toLowerCase() === 'all') {
            RSL.Timeline.clearFilter(element);
        } else {
            var criteria = {};
            criteria[config.filterField] = value;
            RSL.Timeline.filter(element, criteria);
        }
    }

    /**
     * Get event's filter value based on field
     * @private
     */
    function getEventFilterValue(eventEl, field) {
        // Check for data-filter-value first (explicit override)
        var filterValue = eventEl.getAttribute('data-filter-value');
        if (filterValue !== null) {
            return filterValue;
        }

        // Check common fields
        switch (field) {
            case 'status':
                return eventEl.getAttribute('data-status');
            case 'category':
                return eventEl.getAttribute('data-category');
            case 'type':
                return eventEl.getAttribute('data-type');
            default:
                // Try data-{field}
                return eventEl.getAttribute('data-' + field);
        }
    }

    /**
     * Auto-initialize on DOMContentLoaded
     */
    document.addEventListener('DOMContentLoaded', function() {
        RSL.Timeline.init();
    });

    /**
     * Re-initialize for dynamically added content
     */
    if (typeof MutationObserver !== 'undefined') {
        var observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                mutation.addedNodes.forEach(function(node) {
                    if (node.nodeType === 1) {
                        if (node.hasAttribute && node.hasAttribute('data-rsl-timeline')) {
                            RSL.Timeline.create(node);
                        }
                        var nested = node.querySelectorAll && node.querySelectorAll('[data-rsl-timeline]');
                        if (nested) {
                            nested.forEach(function(el) {
                                if (!instances.has(el)) {
                                    RSL.Timeline.create(el);
                                }
                            });
                        }
                    }
                });
            });
        });

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

})();
