export function initTableScrollShadows(root = document) {
    const containerSelector = ".ekesto-responsive-table-inner";
    const scrollHandlers = new Map();

    const getHost = (el) =>
        el.closest(".ekesto-responsive-table-container") || el;

    const update = (el) => {
        const host = getHost(el);
        const max = el.scrollWidth - el.clientWidth;
        const EPS = 2;

        if (max <= EPS) {
            host.classList.remove(
                "scrolling-enabled",
                "scroll-left",
                "scroll-right",
            );
            return;
        }

        host.classList.add("scrolling-enabled");

        const atLeft = el.scrollLeft <= EPS;
        const atRight = el.scrollLeft >= max - EPS;

        host.classList.toggle("scroll-left", !atLeft);
        host.classList.toggle("scroll-right", !atRight);
    };

    const bindContainer = (el) => {
        if (!el || scrollHandlers.has(el)) {
            return;
        }

        update(el);
        el.dataset.scrollShadowBound = "1";

        let ticking = false;
        const onScroll = () => {
            if (ticking) return;
            ticking = true;
            requestAnimationFrame(() => {
                ticking = false;
                update(el);
            });
        };
        scrollHandlers.set(el, onScroll);
        el.addEventListener("scroll", onScroll, { passive: true });
    };

    const unbindContainer = (el) => {
        const onScroll = scrollHandlers.get(el);
        if (onScroll) {
            el.removeEventListener("scroll", onScroll);
        }

        scrollHandlers.delete(el);
        getHost(el).classList.remove(
            "scrolling-enabled",
            "scroll-left",
            "scroll-right",
        );
        delete el.dataset.scrollShadowBound;
    };

    const bindNodeAndDescendants = (node) => {
        if (!(node instanceof Element)) {
            return;
        }

        if (node.matches(containerSelector)) {
            bindContainer(node);
        }

        node.querySelectorAll(containerSelector).forEach(bindContainer);
    };

    const unbindNodeAndDescendants = (node) => {
        if (!(node instanceof Element)) {
            return;
        }

        if (node.matches(containerSelector) && scrollHandlers.has(node)) {
            unbindContainer(node);
        }

        node.querySelectorAll(containerSelector).forEach((el) => {
            if (scrollHandlers.has(el)) {
                unbindContainer(el);
            }
        });
    };

    if (root instanceof Element) {
        bindNodeAndDescendants(root);
    } else if (root && typeof root.querySelectorAll === "function") {
        root.querySelectorAll(containerSelector).forEach(bindContainer);
    }

    // update on resize (covers layout/table changes)
    const onResize = () => {
        scrollHandlers.forEach((_, el) => update(el));
    };
    window.addEventListener("resize", onResize);

    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            mutation.addedNodes.forEach(bindNodeAndDescendants);
            mutation.removedNodes.forEach(unbindNodeAndDescendants);
        });
    });

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

    // return cleanup
    return () => {
        observer.disconnect();
        window.removeEventListener("resize", onResize);
        Array.from(scrollHandlers.keys()).forEach(unbindContainer);
        scrollHandlers.clear();
    };
}
