/**
 * RSL Accessibility Navigator
 *
 * Comprehensive accessibility toolkit providing skip navigation, focus indicators,
 * card accessibility enhancements, text truncation, and slideshow accessibility features.
 * Implements WCAG 2.1 Level AA compliance with keyboard navigation, screen reader support,
 * and visual focus indicators.
 *
 * @fileoverview Accessibility utilities and navigation helpers for RSL components
 * @version 2.0.0
 * @license MIT
 *
 * Key Features:
 * - Skip navigation links (skip-to-content, skip-below, skip-above)
 * - Enhanced focus indicators for keyboard navigation
 * - Accessible alert helpers with ARIA support
 * - Card accessibility with automatic ARIA labeling
 * - Slideshow/carousel accessibility with keyboard controls
 * - Text truncation with CSS line-clamp fallback
 * - Dynamic ARIA attribute management
 *
 * Usage:
 * // Skip Navigation
 * ADAcompliantNavigator({
 *   'id-or-class': '.my-section',
 *   'require-skip-below-link': 'true',
 *   'skip-below-link-text': 'Skip to next section',
 *   'link-class': 'sr-only'
 * });
 *
 * // Alert Helper
 * ADAHelperForAlerts({
 *   location: '#alert-container',
 *   ariaLabel: 'Important: Session expired',
 *   allowClose: true,
 *   additionalClass: 'alert-link'
 * });
 *
 * // Focus Indicator (auto-initialized)
 * focusIndicator();
 *
 * Public API:
 * - ADAcompliantNavigator(options) - Create skip navigation links
 * - ADAHelperForAlerts(options) - Create accessible alerts
 * - focusIndicator() - Initialize focus indicators
 * - truncateText(element, lineCount) - Truncate text with ellipsis
 * - adjustTextOnLoad() - Apply truncation on page load
 * - adjustTextOnResize() - Update truncation on resize
 * - makeSlideshowsAccessible() - Enhance slideshow accessibility
 */

(function() {
    'use strict';

    /**
     * Create accessible skip navigation links for sections
     *
     * Dynamically generates skip-to-content links before and after specified
     * elements to improve keyboard navigation. Creates target anchors if not
     * explicitly provided. Displays links on Tab key press when element has focus
     * or is in viewport.
     *
     * @param {Object} options - Configuration options
     * @param {string} options.id-or-class - CSS selector for target elements
     * @param {string} [options.require-skip-below-link] - 'true' to create skip-below link
     * @param {string} [options.skip-below-link-text] - Text for skip-below link
     * @param {string} [options.set-skip-below-target] - Custom target ID for skip-below
     * @param {string} [options.require-skip-above-link] - 'true' to create skip-above link
     * @param {string} [options.skip-above-link-text] - Text for skip-above link
     * @param {string} [options.set-skip-above-target] - Custom target ID for skip-above
     * @param {string} [options.link-class] - Additional CSS classes for skip links
     *
     * @example
     * ADAcompliantNavigator({
     *   'id-or-class': '.content-section',
     *   'require-skip-below-link': 'true',
     *   'skip-below-link-text': 'Skip to footer',
     *   'link-class': 'sr-only sr-only-focusable'
     * });
     */
    window.ADAcompliantNavigator = function(options) {
        console.log(options);
        const targetElements = document.querySelectorAll(options['id-or-class']);

        targetElements.forEach(function(myDiv, index) {
            // Create skip-below link if requested
            if (options['require-skip-below-link'] === 'true') {
                const skipBelow = document.createElement('a');
                skipBelow.href = options['set-skip-below-target'] ? options['set-skip-below-target'] : '#target-below-' + index;
                skipBelow.id = 'skip-below-' + index;
                skipBelow.className = 'skip-link ' + options['link-class'];
                skipBelow.textContent = options['skip-below-link-text'];
                myDiv.parentNode.insertBefore(skipBelow, myDiv);

                // Create target anchor if not explicitly provided
                if (!options['set-skip-below-target']) {
                    const targetBelow = document.createElement('div');
                    targetBelow.id = 'target-below-' + index;
                    targetBelow.className = 'skip-target';
                    if (myDiv.nextSibling) {
                        myDiv.parentNode.insertBefore(targetBelow, myDiv.nextSibling);
                    } else {
                        myDiv.parentNode.appendChild(targetBelow);
                    }
                }
            }

            // Create skip-above link if requested
            if (options['require-skip-above-link'] === 'true') {
                const skipAbove = document.createElement('a');
                skipAbove.href = options['set-skip-above-target'] ? options['set-skip-above-target'] : '#target-above-' + index;
                skipAbove.id = 'skip-above-' + index;
                skipAbove.className = 'skip-link ' + options['link-class'];
                skipAbove.textContent = options['skip-above-link-text'];
                myDiv.appendChild(skipAbove);

                // Create target anchor if not explicitly provided
                if (!options['set-skip-above-target']) {
                    const targetAbove = document.createElement('div');
                    targetAbove.id = 'target-above-' + index;
                    targetAbove.className = 'skip-target';
                    targetAbove.tabIndex = -1;
                    myDiv.parentNode.insertBefore(targetAbove, myDiv);
                }
            }
        });

        // Show skip links on Tab navigation
        document.addEventListener('keydown', function(event) {
            if (event.key === 'Tab') {
                handleTabNavigation(event);
            }
        });

        /**
         * Handle Tab key navigation and show appropriate skip links
         *
         * @param {KeyboardEvent} event - Keyboard event
         * @private
         */
        function handleTabNavigation(event) {
            const isShiftTab = event.shiftKey;

            targetElements.forEach(function(myDiv, index) {
                const skipBelow = document.getElementById('skip-below-' + index);
                const skipAbove = document.getElementById('skip-above-' + index);

                const myDivHasFocus = myDiv.contains(document.activeElement);

                if (myDivHasFocus) {
                    if (isShiftTab) {
                        if (skipAbove) skipAbove.style.display = 'block';
                    } else {
                        if (skipBelow) skipBelow.style.display = 'block';
                    }
                } else if (isElementInViewport(myDiv)) {
                    if (skipBelow) skipBelow.style.display = 'block';
                }
            });
        }

        /**
         * Check if element is currently in viewport
         *
         * @param {HTMLElement} el - Element to check
         * @returns {boolean} True if element is fully visible in viewport
         * @private
         */
        function isElementInViewport(el) {
            const rect = el.getBoundingClientRect();
            return (
                rect.top >= 0 &&
                rect.left >= 0 &&
                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
                rect.right <= (window.innerWidth || document.documentElement.clientWidth)
            );
        }
    };

    /**
     * Create accessible alert links for screen readers
     *
     * Generates keyboard-accessible alert elements with proper ARIA attributes
     * and optional close functionality. Alerts become visible on Tab key press.
     *
     * @param {Object} options - Alert configuration
     * @param {string} options.location - CSS selector for parent container
     * @param {string} options.ariaLabel - ARIA label text for screen readers
     * @param {boolean} [options.allowClose] - Enable close on Enter key
     * @param {string} [options.additionalClass] - Additional CSS classes
     *
     * @example
     * ADAHelperForAlerts({
     *   location: '#notifications',
     *   ariaLabel: 'Payment successful',
     *   allowClose: true,
     *   additionalClass: 'success-alert'
     * });
     */
    window.ADAHelperForAlerts = function(options) {
        const location = options.location;
        const ariaLabel = options.ariaLabel;
        const allowClose = options.allowClose;

        const parentContainer = document.querySelector(location);
        if (!parentContainer) {
            console.warn('ADAHelperForAlerts: Container not found:', location);
            return;
        }

        // Create container div
        const accessibleDiv = document.createElement('div');

        // Create accessible link element
        const accessibleElement = document.createElement('a');
        accessibleElement.href = '#';
        accessibleElement.className = 'skip-link ' + (options.additionalClass || '');
        accessibleElement.textContent = ariaLabel;
        accessibleElement.tabIndex = 0;
        accessibleElement.setAttribute('aria-label', ariaLabel);

        // Add close functionality if enabled
        if (allowClose === true) {
            accessibleElement.setAttribute('aria-label', 'This is an expired alert, ' + ariaLabel + ' You can now close this alert by pressing enter.');

            accessibleElement.addEventListener('click', function(e) {
                e.preventDefault();
                parentContainer.removeChild(accessibleDiv);
            });
        }

        accessibleDiv.appendChild(accessibleElement);
        parentContainer.appendChild(accessibleDiv);

        // Show on Tab key press
        document.addEventListener('keydown', function(event) {
            if (event.key === 'Tab') {
                accessibleElement.style.display = 'block';
            }
        });
    };

    /**
     * Initialize enhanced focus indicators for keyboard navigation
     *
     * Applies visual focus indicators only for keyboard navigation, not mouse clicks.
     * Supports special 'focus-wrap-parent' class to apply focus styling to parent elements.
     * Uses blue box-shadow for consistent, accessible focus indication.
     *
     * @example
     * // Auto-initialized on page load, or call manually:
     * focusIndicator();
     *
     * // HTML usage:
     * // <button class="focus-wrap-parent">Button</button>
     * // (parent div will receive focus styling instead of button)
     */
    window.focusIndicator = function() {
        let lastInteractionKeyboard = false;
        let shadowedElement = null;

        // Track interaction type
        window.addEventListener('keydown', function() {
            lastInteractionKeyboard = true;
        });

        window.addEventListener('mousedown', function() {
            lastInteractionKeyboard = false;
            // Remove focus styling on mouse interaction
            if (shadowedElement) {
                if (shadowedElement.classList.contains('focus-wrap-parent')) {
                    shadowedElement.parentElement.style.boxShadow = '';
                } else {
                    shadowedElement.style.boxShadow = '';
                }
                shadowedElement = null;
            }
        });

        /**
         * Apply focus styling on keyboard navigation
         */
        window.addEventListener('focusin', function(event) {
            if (!lastInteractionKeyboard) return;

            // Check for parent focus wrapping
            if (event.target.classList.contains('focus-wrap-parent')) {
                event.target.parentElement.style.boxShadow = 'rgba(3, 102, 214, 0.3) 0px 0px 0px 6px';
                shadowedElement = event.target;
            } else {
                event.target.style.boxShadow = 'rgba(3, 102, 214, 0.3) 0px 0px 0px 6px';
                shadowedElement = event.target;
            }
        });

        /**
         * Remove focus styling when element loses focus
         */
        window.addEventListener('focusout', function(event) {
            if (!lastInteractionKeyboard) return;

            if (event.target.classList.contains('focus-wrap-parent')) {
                event.target.parentElement.style.boxShadow = '';
                shadowedElement = null;
            } else {
                event.target.style.boxShadow = '';
                shadowedElement = null;
            }
        });
    };

    /**
     * Enhance cards with accessibility attributes
     *
     * Automatically adds ARIA labels, tabindex, and descriptive text for cards.
     * Handles cards with images, titles, body text, links, and footers.
     * Detects and describes links within cards for screen readers.
     *
     * @private
     */
    document.addEventListener('DOMContentLoaded', () => {
        const cards = document.querySelectorAll('.card[data-tab-card="true"]');

        cards.forEach((card) => {
            /**
             * Extract text content from HTML, removing tags
             *
             * @param {string} html - HTML string
             * @returns {string} Plain text content
             */
            const getTextFromHTML = (html) => {
                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = html;
                return tempDiv.textContent || tempDiv.innerText || '';
            };

            let cardText = '';

            // Gather all card text content
            const cardImgAltText = card.querySelector('.card-img')?.getAttribute('alt') || '';
            const cardTitleText = card.querySelector('.card-title')?.textContent || '';
            const cardBodyText = getTextFromHTML(card.querySelector('.card-body')?.innerHTML || '');
            const cardFooterText = card.querySelector('.card-footer')?.textContent || '';

            cardText = cardImgAltText + ' ' + cardTitleText + ' ' + cardBodyText + ' ' + cardFooterText;

            // Check if card is wrapped in a link
            const isWrappedInLink = card.parentNode.nodeName === 'A';

            // Count and describe links
            const links = card.querySelectorAll('a');
            const linkCount = links.length;
            let linkText = '';

            if (linkCount === 0) {
                linkText = 'Card contains no links. ';
            } else {
                linkText = `Card contains ${linkCount} link${linkCount > 1 ? 's' : ''}. `;
            }

            // Build complete card description
            if (linkCount > 0) {
                cardText = linkText + 'Content of card reads: ' + cardText + ' Press tab to continue to the links in this card.';
            } else {
                cardText = linkText + 'Content of card reads: ' + cardText;
            }

            // Apply ARIA label to card or parent link
            if (isWrappedInLink) {
                card.parentNode.setAttribute('aria-label', cardText);
            } else {
                card.setAttribute('aria-label', cardText);
                card.setAttribute('tabindex', '0');
            }

            // Enhance links with descriptive ARIA labels
            links.forEach((link, index) => {
                if (!link.hasAttribute('aria-label')) {
                    const linkTextContent = link.textContent;
                    const linkHref = link.getAttribute('href');
                    const ariaLabel = `Card Link ${index + 1}: ${linkTextContent}. If you follow this URL, you will be taken to ${linkHref}`;
                    link.setAttribute('aria-label', ariaLabel);
                }

                if (!link.hasAttribute('tabindex')) {
                    link.setAttribute('tabindex', '0');
                }
            });

            // Add aria-describedby for card body
            const cardBody = card.querySelector('.card-body');
            if (cardBody && !cardBody.hasAttribute('id')) {
                const cardDescId = `card_${card.dataset.tabCard}_desc`;
                cardBody.setAttribute('id', cardDescId);
                card.setAttribute('aria-describedby', cardDescId);
            }

            // Handle button in footer
            const button = card.querySelector('.card-footer button');
            if (button && !button.hasAttribute('aria-label')) {
                const buttonText = button.textContent;
                button.setAttribute('aria-label', `Button: ${buttonText}`);
            }
        });
    });

    /**
     * Text truncation module using CSS line-clamp with fallback
     *
     * Provides cross-browser text truncation with ellipsis. Uses native CSS
     * -webkit-line-clamp when available, falls back to manual truncation.
     *
     * @param {HTMLElement} element - Element to truncate
     * @param {number} [lineCount=2] - Number of lines before truncation
     * @returns {Object} Object with 'original' and 'clamped' HTML
     *
     * @example
     * const result = truncateText(element, 3);
     * console.log(result.original); // Original HTML
     * console.log(result.clamped);  // Truncated HTML
     */
    window.truncateText = (function() {
        if (typeof define === 'function' && define.amd) {
            // AMD module support
            return define([], createTruncateText);
        } else if (typeof exports === 'object') {
            // Node/CommonJS support
            return exports.truncateText = createTruncateText();
        } else {
            // Browser global
            return createTruncateText();
        }

        function createTruncateText() {
            return function truncateText(element, lineCount) {
                const clampConfig = {
                    clamp: lineCount || 2,
                    useNativeClamp: true,
                    splitOnChars: ['.', '-', '–', '—', ' '],
                    animate: false,
                    truncationChar: '…',
                    truncationHTML: null
                };

                const originalHTML = element.innerHTML;
                const clampStyle = element.style;
                const supportsNativeClamp = typeof(element.style.webkitLineClamp) !== 'undefined';

                const lineHeight = getComputedStyle(element).lineHeight;
                const computedLineHeight = lineHeight === 'normal'
                    ? parseFloat(getComputedStyle(element).fontSize) * 1.2
                    : parseFloat(lineHeight);

                const maxHeight = computedLineHeight * clampConfig.clamp;

                // Use native CSS line-clamp if available
                if (supportsNativeClamp && clampConfig.useNativeClamp) {
                    clampStyle.overflow = 'hidden';
                    clampStyle.textOverflow = 'ellipsis';
                    clampStyle.webkitBoxOrient = 'vertical';
                    clampStyle.display = '-webkit-box';
                    clampStyle.webkitLineClamp = clampConfig.clamp;
                } else {
                    // Fallback to manual truncation
                    const elementHeight = element.clientHeight;
                    if (elementHeight > maxHeight) {
                        truncateUntilFits(element, maxHeight, clampConfig);
                    }
                }

                return {
                    'original': originalHTML,
                    'clamped': element.innerHTML
                };
            };

            /**
             * Manually truncate text until it fits within max height
             *
             * @param {HTMLElement} element - Element to truncate
             * @param {number} maxHeight - Maximum height in pixels
             * @param {Object} clampConfig - Truncation configuration
             * @private
             */
            function truncateUntilFits(element, maxHeight, clampConfig) {
                let text = element.innerHTML.replace(clampConfig.truncationChar, '');
                let textParts = text.split(' ');

                while (element.clientHeight > maxHeight && textParts.length > 1) {
                    textParts.pop();
                    element.innerHTML = textParts.join(' ') + clampConfig.truncationChar;
                }
            }
        }
    })();

    /**
     * Apply text truncation to elements on page load
     *
     * Finds all elements with data-truncate-lines attribute and applies
     * truncation. Adds 'not-truncated' class if content fits without truncation.
     *
     * @example
     * // HTML: <p data-truncate-lines="3">Long text...</p>
     * adjustTextOnLoad(); // Truncates to 3 lines
     */
    window.adjustTextOnLoad = function() {
        const elements = document.querySelectorAll('[data-truncate-lines]');
        elements.forEach(function(element) {
            const lineCount = parseInt(element.getAttribute('data-truncate-lines'), 10);
            if (!isNaN(lineCount)) {
                const initialHTML = element.innerHTML;
                truncateText(element, lineCount);
                const finalHTML = element.innerHTML;

                const hasOverflow = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
                if (initialHTML === finalHTML && !hasOverflow) {
                    element.classList.add('not-truncated');
                }
            }
        });
    };

    /**
     * Update text truncation on window resize
     *
     * Recalculates truncation when viewport size changes. Updates 'not-truncated'
     * class based on current overflow state.
     *
     * @example
     * window.addEventListener('resize', adjustTextOnResize);
     */
    window.adjustTextOnResize = function() {
        const elements = document.querySelectorAll('[data-truncate-lines]');
        elements.forEach(function(element) {
            const lineCount = parseInt(element.getAttribute('data-truncate-lines'), 10);
            if (!isNaN(lineCount)) {
                const initialHTML = element.innerHTML;
                truncateText(element, lineCount);
                const finalHTML = element.innerHTML;

                const hasOverflow = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
                if (initialHTML === finalHTML && !hasOverflow) {
                    element.classList.add('not-truncated');
                } else {
                    element.classList.remove('not-truncated');
                }
            }
        });
    };

    /**
     * Make slideshows/carousels fully accessible
     *
     * Enhances carousel indicators with ARIA labels, alt text, and keyboard navigation.
     * Hides decorative navigation arrows from screen readers. Extracts slide images and
     * captions for thumbnail previews. Manages focus and tab order for optimal accessibility.
     *
     * Features:
     * - Auto-generates alt text from captions
     * - Creates descriptive ARIA labels for each slide
     * - Enables keyboard navigation via Tab/Enter
     * - Hides mouse-only controls from assistive tech
     * - Extracts slide data for thumbnails and previews
     *
     * @example
     * // HTML: <div class="slider">...</div>
     * makeSlideshowsAccessible(); // Auto-called on DOMContentLoaded
     */
    window.makeSlideshowsAccessible = function() {
        const sliders = Array.from(document.querySelectorAll('.responsive-slot-layout .slider'));

        sliders.forEach((slider, sliderIndex) => {
            const indicatorsContainer = slider.querySelector('.slider-indicators');
            const animationIn = indicatorsContainer?.getAttribute('data-animation-in') || '';
            const indicators = Array.from(slider.querySelectorAll('.slider-indicators-scroll .slider-indicator'));
            const slides = Array.from(slider.querySelectorAll('.carousel-slide'));

            // Assign data-slide-index to each slide
            slides.forEach((slide, index) => {
                slide.setAttribute('data-slide-index', index.toString());

                const image = slide.querySelector('img');
                const captionHeader = slide.querySelector('.carousel-caption h3');
                const captionText = slide.querySelector('.carousel-caption p');

                // Generate alt text from caption if missing
                if (image && !image.alt) {
                    if (captionHeader && captionText) {
                        image.alt = `${captionHeader.innerText} ${captionText.innerText}`;
                    } else if (captionHeader) {
                        image.alt = captionHeader.innerText;
                    } else if (captionText) {
                        image.alt = captionText.innerText;
                    } else {
                        image.alt = `You are on image number ${index + 1} of ${slides.length}. Please contact us to update our alt tags for our slideshow images.`;
                    }
                }

                // Hide caption links from keyboard navigation initially
                const links = slide.querySelectorAll('.carousel-caption a');
                links.forEach(link => {
                    link.setAttribute('aria-hidden', 'true');
                    link.setAttribute('tabindex', '-1');
                });
            });

            /**
             * Update ARIA label for slide indicator
             *
             * @param {HTMLElement} indicator - Slide indicator element
             * @private
             */
            function updateAriaLabel(indicator) {
                const slideIndex = indicator.getAttribute('data-slide-index');
                const slide = slides.find((slide) => slide.getAttribute('data-slide-index') === slideIndex);
                if (!slide) return;

                const imageNumber = parseInt(slideIndex) + 1;
                const altTag = slide.querySelector('img')?.alt || '';
                const captionTitleElement = slide.querySelector('.carousel-caption h3');
                const captionTextElement = slide.querySelector('.carousel-caption p');
                let captionTitle = '';
                let captionText = '';

                // Extract caption HTML and enable links for active slide
                if (captionTitleElement) {
                    captionTitle = captionTitleElement.innerHTML;
                    captionTitleElement.querySelectorAll('a').forEach(link => {
                        link.removeAttribute('aria-hidden');
                        link.removeAttribute('tabindex');
                    });
                }

                if (captionTextElement) {
                    captionText = captionTextElement.innerHTML;
                    captionTextElement.querySelectorAll('a').forEach(link => {
                        link.removeAttribute('aria-hidden');
                        link.removeAttribute('tabindex');
                    });
                }

                const links = slide.querySelectorAll('.carousel-caption a');
                const linksText = links.length > 0
                    ? ' or press enter to view the links within this slideshow image'
                    : '';

                // Build comprehensive ARIA label
                const ariaLabel = `Image ${imageNumber}${(altTag && altTag !== `${captionTitle} ${captionText}`) ? `, Alt Tag: ${altTag}` : ''}${(captionTitle || captionText) ? `, Slideshow Caption: ${captionTitle} ${captionText}` : ''}. Press tab to continue to next slide${linksText}`;

                indicator.setAttribute('aria-label', ariaLabel);
                indicator.setAttribute('data-caption-header', captionTitle);
                indicator.setAttribute('data-caption-text', captionText);
                indicator.setAttribute('data-animation-in', animationIn);

                // Extract slide image for thumbnail
                const imageTag = slide.querySelector('img');
                if (imageTag) {
                    const imageUrl = imageTag.getAttribute('src');
                    const classesToAdd = ['responsive-img', 'max-width-300', 'thumbnail-img'];

                    let classAttribute = '';
                    if (imageTag.hasAttribute('class')) {
                        const existingClasses = imageTag.getAttribute('class').trim();
                        classAttribute = `class="${existingClasses} ${classesToAdd.join(' ')}"`;
                    } else {
                        classAttribute = `class="${classesToAdd.join(' ')}"`;
                    }

                    const slideImgData = `<img src="${imageUrl}" alt="${imageTag.alt}" ${classAttribute}>`;
                    indicator.setAttribute('data-slide-img', slideImgData);
                }
            }

            // Initialize ARIA labels for all indicators
            indicators.forEach(updateAriaLabel);

            // Update ARIA labels on focus
            indicators.forEach(function(indicator) {
                indicator.addEventListener('focus', function() {
                    updateAriaLabel(indicator);
                });

                if (!indicator.hasAttribute('tabIndex')) {
                    indicator.setAttribute('tabIndex', '0');
                }
            });

            // Hide navigation arrows from screen readers and keyboard
            const prevArrow = slider.querySelector('.carousel-prev-arrow');
            const nextArrow = slider.querySelector('.carousel-next-arrow');

            if (prevArrow) {
                prevArrow.setAttribute('aria-hidden', 'true');
                if (!prevArrow.hasAttribute('tabIndex')) {
                    prevArrow.setAttribute('tabIndex', '-1');
                }
            }

            if (nextArrow) {
                nextArrow.setAttribute('aria-hidden', 'true');
                if (!nextArrow.hasAttribute('tabIndex')) {
                    nextArrow.setAttribute('tabIndex', '-1');
                }
            }
        });
    };

    // Auto-initialize slideshow accessibility
    document.addEventListener('DOMContentLoaded', makeSlideshowsAccessible);

})();
