/**
 * p11Modal.js
 * Part of the p11 UI Library.
 * Handles Modal, Offcanvas, and Toast logic including Focus Trapping and Scroll Locking.
 * All comments and documentation are in English.
 */

import { UtilityFunctions } from './p11utility.js';

/* ========================================================================
   INTERNAL STATE & MODULE-LEVEL VARIABLES
   ======================================================================== */

const activeModals = new Set();
const modalStates = new Map();
let lastFocusedElementBeforeModal = null;

// Map for Bootstrap Offcanvas instances (Internal tracking)
const offcanvasInstances = new Map();

/* ========================================================================
   INTERNAL HELPER FUNCTIONS (Private to this module)
   ======================================================================== */

/**
 * Manages body scroll lock and prevents layout shift by compensating for scrollbar width.
 */
function applyBodyScrollLock(shouldLock) {
    UtilityFunctions.log(`applyBodyScrollLock: ${shouldLock}`);

    if (shouldLock) {
        document.body.style.overflow = 'hidden';
        const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
        if (scrollbarWidth > 0) {
            document.body.style.paddingRight = `${scrollbarWidth}px`;
        }
    } else {
        // Only unlock if no other active modal requires the lock
        const anyModalStillNeedsScrollLock = [...modalStates.values()].some(s => s.preventScroll);
        if (!anyModalStillNeedsScrollLock) {
            document.body.style.overflow = '';
            document.body.style.paddingRight = '';
        }
    }
}

/**
 * Stack-independent scroll lock for simple overlays.
 * Erlaubt das Entsperren ohne Prüfung des modalStates Stacks.
 */
/**
 * Stack-independent scroll lock for simple overlays.
 */
function applySimpleScrollLock(shouldLock) {
    UtilityFunctions.log(`applySimpleScrollLock: ${shouldLock}`);

    if (shouldLock) {
        // Wir setzen es mit !important, um alles andere zu übersteuern
        document.body.style.setProperty('overflow', 'hidden', 'important');

        const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
        if (scrollbarWidth > 0) {
            document.body.style.setProperty('padding-right', `${scrollbarWidth}px`, 'important');
        }
    } else {
        // 1. Erst die Properties komplett entfernen
        document.body.style.removeProperty('overflow');
        document.body.style.removeProperty('padding-right');

        // 2. EXPLIZIT auf 'visible' oder 'auto' zwingen, falls CSS-Klassen noch 'hidden' erzwingen
        // Wir nutzen 'unset', um den Browser-Standard wiederherzustellen
        document.body.style.setProperty('overflow', 'visible', 'important');

        // 3. Nach einem minimalen Tick das 'visible !important' wieder entfernen, 
        // damit der Body wieder sauber für andere Komponenten (echte Modale) ist.
        setTimeout(() => {
            document.body.style.removeProperty('overflow');
            document.body.style.removeProperty('padding-right');
            UtilityFunctions.log("applySimpleScrollLock: Final cleanup executed.");
        }, 10);
    }
}

/**
 * Global keydown handler for Escape key and Tab focus trapping.
 */
function handleGlobalKeydown(event) {
    const topModalEntry = [...modalStates.values()].pop();
    if (!topModalEntry) return;

    const { element: topModalElement, dotNetRef, enableFocusTrap } = topModalEntry;

    if (event.key === 'Escape') {
        event.preventDefault();
        if (dotNetRef && dotNetRef.invokeMethodAsync) {
            UtilityFunctions.log("Global Keydown: Escape pressed. Invoking HandleEscapeKey.");
            dotNetRef.invokeMethodAsync('HandleEscapeKey');
        }
    }

    if (event.key === 'Tab' && enableFocusTrap) {
        const focusableElements = UtilityFunctions.getFocusableElements(topModalElement);
        if (focusableElements.length === 0) {
            event.preventDefault();
            return;
        }

        const firstFocusable = focusableElements[0];
        const lastFocusable = focusableElements[focusableElements.length - 1];

        if (event.shiftKey) { // Shift + Tab
            if (document.activeElement === firstFocusable || !topModalElement.contains(document.activeElement)) {
                lastFocusable.focus();
                event.preventDefault();
            }
        } else { // Tab
            if (document.activeElement === lastFocusable || !topModalElement.contains(document.activeElement)) {
                firstFocusable.focus();
                event.preventDefault();
            }
        }
    }
}

/**
 * Global click handler for outside-click detection.
 */
function handleGlobalClick(event) {
    const topModalEntry = [...modalStates.values()].pop();
    if (!topModalEntry) return;

    const { element: topModalElement, dotNetRef, closeOnOutsideClick } = topModalEntry;

    if (closeOnOutsideClick && dotNetRef && dotNetRef.invokeMethodAsync) {
        if (!topModalElement.contains(event.target)) {
            UtilityFunctions.log("Global Click: Outside click detected. Invoking HandleOutsideClick.");
            dotNetRef.invokeMethodAsync('HandleOutsideClick');
        }
    }
}

/* ========================================================================
   PUBLIC EXPORTED FUNCTIONS
   ======================================================================== */

export const P11ModalFunctions = {

    // --- MODAL & FOCUS MANAGEMENT ---

    focusModalAndTrap: async function (modalElementRef, dotNetRef, restoreFocusId, preventScroll, enableFocusTrap = true, closeOnOutsideClick = true) {
        UtilityFunctions.log("focusModalAndTrap: Activating focus management.", { preventScroll, enableFocusTrap });

        if (!modalElementRef || activeModals.has(modalElementRef)) return;

        // Save focus before opening the first modal
        if (activeModals.size === 0) {
            lastFocusedElementBeforeModal = document.activeElement;
        }

        modalStates.set(modalElementRef, { element: modalElementRef, dotNetRef, preventScroll, enableFocusTrap, closeOnOutsideClick, restoreFocusId });
        activeModals.add(modalElementRef);

        if (preventScroll) applyBodyScrollLock(true);

        if (enableFocusTrap) {
            setTimeout(() => {
                const focusable = UtilityFunctions.getFocusableElements(modalElementRef);
                focusable.length > 0 ? focusable[0].focus() : modalElementRef.focus();
            }, 50);
        }

        if (activeModals.size === 1) {
            document.addEventListener('keydown', handleGlobalKeydown);
            document.addEventListener('click', handleGlobalClick);
        }
    },

    releaseModalFocus: async function (modalElementRef, preventScroll) {
        UtilityFunctions.log("releaseModalFocus: Releasing focus.");

        if (!modalElementRef || !activeModals.has(modalElementRef)) return;

        activeModals.delete(modalElementRef);
        modalStates.delete(modalElementRef);

        if (activeModals.size === 0) {
            document.removeEventListener('keydown', handleGlobalKeydown);
            document.removeEventListener('click', handleGlobalClick);
            applyBodyScrollLock(false);

            // Restore focus
            if (lastFocusedElementBeforeModal && UtilityFunctions.isFocusableAndVisible(lastFocusedElementBeforeModal)) {
                lastFocusedElementBeforeModal.focus();
            } else {
                document.body.focus();
            }
            lastFocusedElementBeforeModal = null;
        } else {
            // Re-evaluate scroll lock for remaining modals
            const anyModalPreventsScrollNow = [...modalStates.values()].some(s => s.preventScroll);
            applyBodyScrollLock(anyModalPreventsScrollNow);

            // Focus new top modal
            const topModalEntry = [...modalStates.values()].pop();
            if (topModalEntry && topModalEntry.enableFocusTrap) {
                const focusable = UtilityFunctions.getFocusableElements(topModalEntry.element);
                focusable.length > 0 ? focusable[0].focus() : topModalEntry.element.focus();
            }
        }
    },

    // --- BOOTSTRAP OFFCANVAS ---

    initializeOffcanvas: function (elementRef, dotNetHelper) {
        if (!elementRef || !window.bootstrap?.Offcanvas) return null;

        let instance = window.bootstrap.Offcanvas.getInstance(elementRef);
        if (!instance) {
            instance = new window.bootstrap.Offcanvas(elementRef);

            const hiddenHandler = () => {
                UtilityFunctions.log(`Offcanvas ${elementRef.id} hidden. Notifying Blazor.`);
                dotNetHelper.invokeMethodAsync('OnBootstrapHidden').catch(err => UtilityFunctions.error(err));
            };

            elementRef.addEventListener('hidden.bs.offcanvas', hiddenHandler);

            offcanvasInstances.set(elementRef.id, { instance, dotNetHelper, hiddenHandler });
        }
        return instance;
    },

    disposeOffcanvas: function (elementId) {
        const entry = offcanvasInstances.get(elementId);
        if (entry) {
            const { instance, hiddenHandler } = entry;
            if (instance._element && hiddenHandler) {
                instance._element.removeEventListener('hidden.bs.offcanvas', hiddenHandler);
            }
            instance.dispose();
            offcanvasInstances.delete(elementId);
            UtilityFunctions.log(`Offcanvas ${elementId} disposed.`);
        }
    },

    // --- BOOTSTRAP TOAST ---

    initializeToast: function (elementRef, dotNetRef, toastId, options) {
        if (!elementRef || !window.bootstrap?.Toast) return null;

        let instance = window.bootstrap.Toast.getInstance(elementRef);
        if (!instance) {
            instance = new window.bootstrap.Toast(elementRef, options || {});
            elementRef.addEventListener('hidden.bs.toast', () => {
                if (dotNetRef) dotNetRef.invokeMethodAsync('JsToastHidden', toastId);
            });
        }
        return instance;
    },

    showToast: function (elementRef) {
        const instance = window.bootstrap.Toast.getInstance(elementRef);
        if (instance) instance.show();
    },

    hideToast: function (elementRef) {
        const instance = window.bootstrap.Toast.getInstance(elementRef);
        if (instance) instance.hide();
    },

    disposeToast: function (elementRef) {
        if (!elementRef || !window.bootstrap?.Toast) return;
        try {
            const instance = window.bootstrap.Toast.getInstance(elementRef);
            if (instance) instance.dispose();
        } catch (err) {
            UtilityFunctions.error("Error disposing toast", err);
        }
    },

    // --- LIGHTWEIGHT OVERLAY (No Stack Management) ---

    /**
     * Shows a lightweight overlay without registering it in the global modal stack.
     */
    showSimpleOverlay: function (elementRef, preventScroll) {
        UtilityFunctions.log("showSimpleOverlay: Showing without stack registration.");
        if (!elementRef) return;

        if (preventScroll) {
            // Nutzt den neuen, stack-unabhängigen Helper
            applySimpleScrollLock(true);
        }

        setTimeout(() => {
            const focusable = UtilityFunctions.getFocusableElements(elementRef);
            if (focusable.length > 0) {
                focusable[0].focus();
            } else {
                elementRef.focus();
            }
        }, 50);
    },

    /**
     * Hides the lightweight overlay and ALWAYS restores body scroll.
     * We remove the parameter check to ensure the body never stays locked by accident.
     */
    hideSimpleOverlay: function () {
        UtilityFunctions.log("hideSimpleOverlay: Hiding and forcing scroll restoration.");

        // Wir rufen den Unlocker IMMER auf. 
        // Er löscht die Styles, was sicher ist, auch wenn sie nie gesetzt wurden.
        applySimpleScrollLock(false);
    },

    // --- LEGACY BRIDGE REGISTRATION ---

    registerLegacyFunctions: function () {
        if (!window.TpcModalJs) window.TpcModalJs = {};

        window.TpcModalJs.focusModalAndTrap = (m, d, r, p, e, c) => this.focusModalAndTrap(m, d, r, p, e, c);
        window.TpcModalJs.releaseModalFocus = (m, p) => this.releaseModalFocus(m, p);

        window.TpcModalJs.forceGlobalReset = () => this.forceGlobalReset();

        window.TpcModalJs.initializeOffcanvas = (el, d) => this.initializeOffcanvas(el, d);
        window.TpcModalJs.disposeOffcanvas = (id) => this.disposeOffcanvas(id);

        window.TpcModalJs.initializeToast = (el, d, id, o) => this.initializeToast(el, d, id, o);
        window.TpcModalJs.showToast = (el) => this.showToast(el);
        window.TpcModalJs.hideToast = (el) => this.hideToast(el);
        window.TpcModalJs.disposeToast = (el) => this.disposeToast(el);

        UtilityFunctions.log("Legacy TpcModalJs functions registered in p11Modal.js.");
    },

    // --- RESET ---

    /**
     * Forcefully clears ALL modal states and global listeners.
     * This is the "Emergency Brake" to restore UI interactivity.
     */
    forceGlobalReset: function () {
        UtilityFunctions.log("forceGlobalReset: Emergency cleanup.");

        // 1. Alle globalen Listener weg
        document.removeEventListener('keydown', handleGlobalKeydown);
        document.removeEventListener('click', handleGlobalClick);

        // 2. State leeren
        activeModals.clear();
        modalStates.clear();

        // 3. UI-Blockaden gewaltsam lösen
        document.body.style.overflow = '';
        document.body.style.paddingRight = '';

        // NEU: Verwaiste graue Schleier (Backdrops) sofort löschen
        document.querySelectorAll('.modal-backdrop, .offcanvas-backdrop').forEach(el => el.remove());

        lastFocusedElementBeforeModal = null;
    },
};

// Auto-register legacy support
P11ModalFunctions.registerLegacyFunctions();