/**
 * RSL Off-Canvas Panel Component
 *
 * Sliding side panel component with overlay, focus trap, and customizable theming.
 * Provides accessible slide-out panels for navigation, filters, or additional content
 * with full keyboard support and ARIA compliance.
 *
 * @fileoverview Accessible off-canvas slide panel with focus management
 * @version 1.0.0
 * @license MIT
 *
 * Key Features:
 * - Slide-in panels from left or right
 * - Overlay backdrop with click-to-close
 * - Focus trap when panel is open
 * - Keyboard navigation (Escape to close, Tab trap)
 * - Customizable colors and size via data attributes
 * - Body scroll lock when panel is open
 * - Automatic focus restoration on close
 * - ARIA attributes for accessibility
 * - Multiple panel support (one open at a time)
 *
 * Usage:
 * // Basic off-canvas panel:
 * <button onclick="toggleOffCanvas('myPanel')">Open Panel</button>
 *
 * <div id="myPanel" class="off-canvas" data-position="right">
 *   <button class="off-canvas-close" onclick="closeAllOffCanvas()">×</button>
 *   <h2>Panel Title</h2>
 *   <p>Panel content...</p>
 * </div>
 *
 * <div id="offCanvasOverlay" class="off-canvas-overlay"></div>
 *
 * // With custom theming:
 * <div id="myPanel" class="off-canvas"
 *      data-position="right"
 *      data-off-canvas-bg="#1a1a1a"
 *      data-off-canvas-heading="#ffffff"
 *      data-off-canvas-text="#cccccc"
 *      data-off-canvas-size="400px"
 *      data-off-canvas-shadow="rgba(0,0,0,0.5)">
 * </div>
 *
 * // Disable edge shadow:
 * <div id="myPanel" class="off-canvas" data-off-canvas-shadow="none">
 * </div>
 *
 * Public API:
 * - toggleOffCanvas(id) - Toggle panel open/closed
 * - closeAllOffCanvas() - Close all open panels
 */

(function() {
    'use strict';

    const body = document.body;
    const overlay = document.getElementById('offCanvasOverlay');

    /**
     * Selector for focusable elements
     * @const {string}
     */
    const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';

    /**
     * Focus management state
     * @type {Object}
     * @property {HTMLElement|null} activePanel - Currently open panel
     * @property {Array<HTMLElement>} activeFocusables - Focusable elements in active panel
     * @property {HTMLElement|null} previousFocus - Element that had focus before panel opened
     */
    let activePanel = null;
    let activeFocusables = [];
    let previousFocus = null;

    /**
     * Get all off-canvas panels in the document
     *
     * @returns {NodeList} Collection of off-canvas panel elements
     *
     * @example
     * const panels = getPanels();
     * console.log(`Found ${panels.length} panels`);
     */
    function getPanels() {
        return document.querySelectorAll('.off-canvas');
    }

    /**
     * Set ARIA hidden attribute on panel
     *
     * @param {HTMLElement} panel - The panel element
     * @param {boolean} hidden - Whether panel should be hidden
     *
     * @example
     * setPanelHidden(panel, true);  // aria-hidden="true"
     * setPanelHidden(panel, false); // aria-hidden="false"
     */
    function setPanelHidden(panel, hidden) {
        panel.setAttribute('aria-hidden', hidden ? 'true' : 'false');
    }

    /**
     * Initialize panel theming from data attributes
     *
     * Applies custom CSS custom properties for background color, text color,
     * heading color, panel size, and edge shadow based on data attributes.
     *
     * @param {HTMLElement} panel - The panel element to theme
     *
     * @example
     * <div class="off-canvas"
     *      data-off-canvas-bg="#333"
     *      data-off-canvas-heading="#fff"
     *      data-off-canvas-text="#ccc"
     *      data-off-canvas-size="500px"
     *      data-off-canvas-shadow="rgba(0,0,0,0.3)">
     * </div>
     * initPanelTheme(panel);
     */
    function initPanelTheme(panel) {
        const ds = panel.dataset;

        if (ds.offCanvasBg) {
            panel.style.setProperty('--off-canvas-bg', ds.offCanvasBg);
        }
        if (ds.offCanvasHeading) {
            panel.style.setProperty('--off-canvas-heading', ds.offCanvasHeading);
        }
        if (ds.offCanvasText) {
            panel.style.setProperty('--off-canvas-paragraph', ds.offCanvasText);
        }
        if (ds.offCanvasSize) {
            panel.style.setProperty('--off-canvas-size', ds.offCanvasSize);
        }

        if (ds.offCanvasShadow) {
            if (ds.offCanvasShadow === 'none') {
                panel.style.setProperty('--off-canvas-shadow-color', 'transparent');
            } else {
                panel.style.setProperty('--off-canvas-shadow-color', ds.offCanvasShadow);
            }
        }
    }

    /**
     * Handle keydown events for focus trap
     *
     * Traps Tab and Shift+Tab within the active panel, cycling focus
     * between first and last focusable elements. Returns focus to panel
     * if it somehow escapes.
     *
     * @param {KeyboardEvent} event - The keydown event
     *
     * @example
     * document.addEventListener('keydown', handleFocusTrapKeydown);
     */
    function handleFocusTrapKeydown(event) {
        if (!activePanel) return;
        if (event.key !== 'Tab') return;

        const focusables = activeFocusables && activeFocusables.length
            ? activeFocusables
            : Array.from(activePanel.querySelectorAll(FOCUSABLE_SELECTOR));

        if (!focusables.length) return;

        const first = focusables[0];
        const last = focusables[focusables.length - 1];

        if (!activePanel.contains(document.activeElement)) {
            first.focus();
            event.preventDefault();
            return;
        }

        if (event.shiftKey) {
            if (document.activeElement === first) {
                last.focus();
                event.preventDefault();
            }
        } else {
            if (document.activeElement === last) {
                first.focus();
                event.preventDefault();
            }
        }
    }

    /**
     * Activate focus trap for panel
     *
     * Sets up focus trap by storing focusable elements, focusing first element,
     * and attaching keydown listener.
     *
     * @param {HTMLElement} panel - The panel element
     *
     * @example
     * activateFocusTrap(panel);
     * // Focus is now trapped within panel
     */
    function activateFocusTrap(panel) {
        activePanel = panel;
        activeFocusables = Array.from(panel.querySelectorAll(FOCUSABLE_SELECTOR));

        if (activeFocusables.length) {
            activeFocusables[0].focus();
        }

        document.addEventListener('keydown', handleFocusTrapKeydown);
    }

    /**
     * Deactivate focus trap and restore previous focus
     *
     * Removes focus trap, clears state, and returns focus to element
     * that had focus before panel was opened.
     *
     * @example
     * deactivateFocusTrap();
     * // Focus trap removed, focus restored to trigger button
     */
    function deactivateFocusTrap() {
        activePanel = null;
        activeFocusables = [];
        document.removeEventListener('keydown', handleFocusTrapKeydown);

        if (previousFocus && typeof previousFocus.focus === 'function') {
            previousFocus.focus();
        }
        previousFocus = null;
    }

    /**
     * Close all off-canvas panels
     *
     * Closes all open panels, removes overlay, unlocks body scroll,
     * and deactivates focus trap. This is the main cleanup function.
     *
     * @public
     *
     * @example
     * closeAllOffCanvas();
     * // All panels closed, focus restored
     */
    window.closeAllOffCanvas = function() {
        getPanels().forEach(panel => {
            panel.classList.remove('off-canvas-open');
            setPanelHidden(panel, true);
        });

        body.classList.remove('scroll-lock');

        if (overlay) {
            overlay.classList.remove('off-canvas-overlay-visible');
            overlay.setAttribute('aria-hidden', 'true');
        }

        deactivateFocusTrap();
    };

    /**
     * Toggle off-canvas panel open/closed
     *
     * Opens the specified panel (closing others) or closes it if already open.
     * Manages overlay, body scroll lock, focus trap, and ARIA attributes.
     *
     * @public
     * @param {string} id - The ID of the panel element to toggle
     *
     * @example
     * // In HTML:
     * <button onclick="toggleOffCanvas('myPanel')">Open</button>
     * <div id="myPanel" class="off-canvas">...</div>
     *
     * // In JavaScript:
     * toggleOffCanvas('myPanel');
     */
    window.toggleOffCanvas = function(id) {
        const panel = document.getElementById(id);
        if (!panel) return;

        const isOpen = panel.classList.contains('off-canvas-open');

        if (isOpen) {
            window.closeAllOffCanvas();
            return;
        }

        if (!activePanel) {
            const currentActive = document.activeElement;
            if (currentActive && currentActive instanceof HTMLElement) {
                previousFocus = currentActive;
            }
        }

        getPanels().forEach(p => {
            if (p !== panel) {
                p.classList.remove('off-canvas-open');
                setPanelHidden(p, true);
            }
        });

        panel.classList.add('off-canvas-open');
        setPanelHidden(panel, false);

        body.classList.add('scroll-lock');

        if (overlay) {
            overlay.classList.add('off-canvas-overlay-visible');
            overlay.setAttribute('aria-hidden', 'false');
        }

        activateFocusTrap(panel);
    };

    /**
     * Handle Escape key to close panels
     *
     * Listens for Escape key globally and closes all panels when pressed.
     */
    document.addEventListener('keydown', function(event) {
        if (event.key === 'Escape' || event.key === 'Esc') {
            window.closeAllOffCanvas();
        }
    });

    /**
     * Mark document as ready for off-canvas functionality
     *
     * Adds .rsl-layout-ready class to body, allowing CSS to hide
     * no-JS fallback content.
     */
    document.addEventListener('DOMContentLoaded', function() {
        body.classList.add('rsl-layout-ready');

        getPanels().forEach(initPanelTheme);
    });

})();
