/**
 * GGM Commerce — Product Carousel Engine
 *
 * Standalone carousel handler for the Product Carousel premium widget.
 * Supports multi-slide display, peek/partial visibility of adjacent slides,
 * infinite loop via cloned nodes, touch/swipe, autoplay, and responsive breakpoints.
 *
 * @package GGM_Commerce
 */

(function ($) {
    'use strict';

    function GgmCommerceProductCarouselHandler($scope) {
        var container = $scope.find('.ggmc-product-carousel');
        if (!container.length) return;

        var viewport = container.find('.ggmc-carousel-viewport');
        var track    = container.find('.ggmc-carousel-track');
        var slides   = track.children('.ggmc-carousel-slide');
        var realSlides = slides.not('.is-clone');
        var totalReal  = realSlides.length;

        if (totalReal === 0) return;

        // Parse config
        var config;
        try {
            config = JSON.parse(container.attr('data-carousel-config'));
        } catch (e) {
            return;
        }

        // State
        var currentIndex   = 0;
        var slideWidth      = 0;
        var isTransitioning = false;
        var autoplayTimer   = null;
        var clonesPerSide   = 0;
        var currentConfig   = {};

        // Count clones
        var clonesBefore = track.children('.ggmc-carousel-slide.is-clone').length;
        // Clones are split: first batch at start, second at end
        // The render puts (spv+1) clones from end at start, then (spv+1) clones from start at end
        // We detect by counting .is-clone elements before the first non-clone
        clonesPerSide = 0;
        slides.each(function (i) {
            if ($(this).hasClass('is-clone')) {
                clonesPerSide++;
            } else {
                return false; // break
            }
        });

        // ── Responsive config ────────────────────────────────
        function getResponsiveConfig() {
            var w = window.innerWidth;
            var base = {
                slidesPerView:  config.slidesPerView || 3,
                slideGap:       config.slideGap || 20,
                peekWidth:      config.peekWidth || 40
            };

            if (w <= 767 && config.responsive && config.responsive.mobile) {
                return $.extend({}, base, config.responsive.mobile);
            }
            if (w <= 1024 && config.responsive && config.responsive.tablet) {
                return $.extend({}, base, config.responsive.tablet);
            }
            return base;
        }

        // ── Calculate dimensions ─────────────────────────────
        function calculateDimensions() {
            currentConfig = getResponsiveConfig();
            var spv  = currentConfig.slidesPerView;
            var gap  = currentConfig.slideGap;
            var peek = currentConfig.peekWidth;

            var viewportWidth = viewport.outerWidth();

            // Available width after peek padding on both sides
            var availableWidth = viewportWidth - (2 * peek);

            // Each slide width: account for gaps between visible slides
            slideWidth = (availableWidth - (gap * (spv - 1))) / spv;

            // Apply to all slides
            slides.each(function () {
                $(this).css({
                    width:       slideWidth + 'px',
                    marginRight: gap + 'px',
                    flexShrink:  0
                });
            });

            // Set viewport padding for peek effect
            viewport.css({
                paddingLeft:  peek + 'px',
                paddingRight: peek + 'px',
                overflow: config.overflowVisible ? 'visible' : 'hidden'
            });
        }

        // ── Go to slide ──────────────────────────────────────
        function goToSlide(index, animate) {
            if (isTransitioning && animate) return;

            var gap = currentConfig.slideGap || 0;
            var translateX = -((index + clonesPerSide) * (slideWidth + gap));

            if (animate) {
                isTransitioning = true;
                track.css({
                    transition: 'transform ' + (config.speed || 400) + 'ms ' + (config.easing || 'ease'),
                    transform:  'translateX(' + translateX + 'px)'
                });
            } else {
                track.css({
                    transition: 'none',
                    transform:  'translateX(' + translateX + 'px)'
                });
            }

            currentIndex = index;
            updateDots();
            updateSlideClasses();
        }

        // ── Navigation ───────────────────────────────────────
        function next() {
            var scroll = config.slidesToScroll || 1;
            goToSlide(currentIndex + scroll, true);
        }

        function prev() {
            var scroll = config.slidesToScroll || 1;
            goToSlide(currentIndex - scroll, true);
        }

        // ── Infinite loop boundary handling ───────────────────
        track.on('transitionend', function () {
            isTransitioning = false;

            if (!config.loop) return;

            if (currentIndex >= totalReal) {
                goToSlide(0, false);
            } else if (currentIndex < 0) {
                goToSlide(totalReal - (config.slidesToScroll || 1), false);
            }
        });

        // Fallback timeout in case transitionend doesn't fire
        function transitionFallback() {
            setTimeout(function () {
                if (isTransitioning) {
                    isTransitioning = false;
                    if (config.loop) {
                        if (currentIndex >= totalReal) {
                            goToSlide(0, false);
                        } else if (currentIndex < 0) {
                            goToSlide(totalReal - 1, false);
                        }
                    }
                }
            }, (config.speed || 400) + 100);
        }

        // ── Update dot indicators ────────────────────────────
        function updateDots() {
            var dots = container.find('.ggmc-carousel-dot');
            if (!dots.length) return;

            var scroll    = config.slidesToScroll || 1;
            var realIndex = ((currentIndex % totalReal) + totalReal) % totalReal;
            var dotIndex  = Math.floor(realIndex / scroll);

            dots.removeClass('active');
            dots.eq(dotIndex).addClass('active');
        }

        // ── Update slide classes (active / peek) ─────────────
        function updateSlideClasses() {
            slides.removeClass('is-active is-peek');

            var spv = currentConfig.slidesPerView || 1;
            var startIdx = currentIndex + clonesPerSide;

            // Mark visible slides
            for (var i = 0; i < spv; i++) {
                var idx = startIdx + i;
                if (idx >= 0 && idx < slides.length) {
                    slides.eq(idx).addClass('is-active');
                }
            }

            // Mark peek slides (one before and one after visible set)
            var peekLeft  = startIdx - 1;
            var peekRight = startIdx + spv;
            if (peekLeft >= 0 && peekLeft < slides.length) {
                slides.eq(peekLeft).addClass('is-peek');
            }
            if (peekRight >= 0 && peekRight < slides.length) {
                slides.eq(peekRight).addClass('is-peek');
            }
        }

        // ── Autoplay ─────────────────────────────────────────
        function startAutoplay() {
            if (!config.autoplay) return;
            stopAutoplay();
            autoplayTimer = setInterval(function () {
                next();
                transitionFallback();
            }, config.autoplaySpeed || 3000);
        }

        function stopAutoplay() {
            if (autoplayTimer) {
                clearInterval(autoplayTimer);
                autoplayTimer = null;
            }
        }

        if (config.pauseOnHover) {
            container.on('mouseenter', stopAutoplay);
            container.on('mouseleave', startAutoplay);
        }

        // ── Touch / Swipe (Pointer Events) ───────────────────
        var drag = {
            active:         false,
            startX:         0,
            startTranslate: 0,
            moved:          false
        };

        function getCurrentTranslateX() {
            var matrix = track.css('transform');
            if (!matrix || matrix === 'none') return 0;
            var values = matrix.match(/matrix.*\((.+)\)/);
            if (!values) return 0;
            var parts = values[1].split(', ');
            return parseFloat(parts[4]) || 0;
        }

        viewport[0].addEventListener('pointerdown', function (e) {
            if (isTransitioning) return;
            drag.active         = true;
            drag.startX         = e.clientX;
            drag.startTranslate = getCurrentTranslateX();
            drag.moved          = false;
            track.css('transition', 'none');
            stopAutoplay();
        });

        viewport[0].addEventListener('pointermove', function (e) {
            if (!drag.active) return;
            var diff = e.clientX - drag.startX;
            if (Math.abs(diff) > 5) {
                drag.moved = true;
            }
            track.css('transform', 'translateX(' + (drag.startTranslate + diff) + 'px)');
        });

        viewport[0].addEventListener('pointerup', function (e) {
            if (!drag.active) return;
            drag.active = false;

            var diff      = e.clientX - drag.startX;
            var threshold = slideWidth * 0.2;

            if (Math.abs(diff) > threshold) {
                if (diff > 0) {
                    prev();
                } else {
                    next();
                }
                transitionFallback();
            } else {
                goToSlide(currentIndex, true);
            }

            startAutoplay();
        });

        viewport[0].addEventListener('pointerleave', function () {
            if (!drag.active) return;
            drag.active = false;
            goToSlide(currentIndex, true);
            startAutoplay();
        });

        // Prevent link clicks after drag
        viewport[0].addEventListener('click', function (e) {
            if (drag.moved) {
                e.preventDefault();
                e.stopPropagation();
                drag.moved = false;
            }
        }, true);

        // ── Arrow click handlers ─────────────────────────────
        container.find('.ggmc-carousel-prev').on('click', function (e) {
            e.preventDefault();
            prev();
            transitionFallback();
        });

        container.find('.ggmc-carousel-next').on('click', function (e) {
            e.preventDefault();
            next();
            transitionFallback();
        });

        // ── Dot click handlers ───────────────────────────────
        container.find('.ggmc-carousel-dot').on('click', function (e) {
            e.preventDefault();
            var scroll   = config.slidesToScroll || 1;
            var dotIndex = parseInt($(this).data('index'), 10);
            goToSlide(dotIndex * scroll, true);
            transitionFallback();
        });

        // ── Keyboard navigation ──────────────────────────────
        container.attr('tabindex', '0');
        container.on('keydown', function (e) {
            if (e.key === 'ArrowLeft') {
                e.preventDefault();
                prev();
                transitionFallback();
            } else if (e.key === 'ArrowRight') {
                e.preventDefault();
                next();
                transitionFallback();
            }
        });

        // ── Reduced motion ───────────────────────────────────
        if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
            config.speed    = 0;
            config.autoplay = false;
        }

        // ── Window resize (debounced) ────────────────────────
        var resizeTimer;
        $(window).on('resize', function () {
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(function () {
                // Re-count slides (Elementor may have re-rendered)
                slides = track.children('.ggmc-carousel-slide');
                calculateDimensions();
                goToSlide(currentIndex, false);
            }, 150);
        });

        // ── Initialise ──────────────────────────────────────
        calculateDimensions();
        goToSlide(0, false);
        startAutoplay();
    }

    // ── Elementor frontend hook ──────────────────────────────
    $(window).on('elementor/frontend/init', function () {
        elementorFrontend.hooks.addAction(
            'frontend/element_ready/ggmc-product-carousel.default',
            GgmCommerceProductCarouselHandler
        );
    });

})(jQuery);
