/**
 * Sifency 3D Arc Carousel — Frontend JS v3
 *
 * Responsive breakpoint support for all Elementor device sizes:
 *   widescreen | desktop | laptop | tablet_extra | tablet | mobile_extra | mobile
 *
 * All layout-affecting config values (slidesPerView, spaceBetween,
 * arcRotate, arcDepth, arcOffset, circleRadius, circleTilt,
 * circleTopRotate, circleTopDepth, circleTopScale) are now stored as
 * per-device maps and resolved at runtime via sarcGetResponsiveValue().
 *
 * Layouts:
 *   3d-arc      — parabolic arch (Swiper coverflow + vertical offset)
 *   clip-path   — clip-path morphing with shape presets + per-slide overrides
 *   circle-top  — top-view fan carousel (fan perspective)
 *   3d-circle   — full 3D circular ring (fixed with virtualTranslate)
 */
(function ($) {
    'use strict';

    /* ── Elementor Breakpoint Map ────────────────────────────────────────── */
    /*
     * Elementor 3.x+ breakpoints (default values in px):
     *   widescreen   ≥ 2400
     *   desktop      ≥ 1025  (the Elementor "desktop" is the base/default)
     *   laptop       ≥ 1366  (some installs differ; use Elementor config when available)
     *   tablet_extra ≥ 880
     *   tablet       ≥ 768
     *   mobile_extra ≥ 480
     *   mobile       <  768  (below tablet)
     *
     * We read the live breakpoints from elementorFrontend.config if available,
     * then fall back to these sensible defaults.
     */
    var SARC_BREAKPOINTS = null;

    function getSarcBreakpoints() {
        if (SARC_BREAKPOINTS) { return SARC_BREAKPOINTS; }

        var bp = { widescreen: 2400, laptop: 1366, tablet_extra: 880, tablet: 768, mobile_extra: 480 };

        if (
            typeof elementorFrontend !== 'undefined' &&
            elementorFrontend.config &&
            elementorFrontend.config.responsive &&
            elementorFrontend.config.responsive.breakpoints
        ) {
            var elBP = elementorFrontend.config.responsive.breakpoints;
            Object.keys(elBP).forEach(function (key) {
                if (elBP[key] && elBP[key].value) {
                    bp[key] = parseInt(elBP[key].value, 10);
                }
            });
        }

        SARC_BREAKPOINTS = bp;
        return SARC_BREAKPOINTS;
    }

    /**
     * Resolve a responsive config value at the current viewport width.
     *
     * @param {Object|number} map  Per-device map or plain scalar.
     * @param {number}        [fallback=0]
     * @returns {number}
     */
    function sarcGetResponsiveValue(map, fallback) {
        fallback = (fallback !== undefined) ? fallback : 0;
        if (typeof map !== 'object' || map === null) {
            return (map !== undefined && map !== null) ? map : fallback;
        }

        var bp = getSarcBreakpoints();
        var vw = window.innerWidth || document.documentElement.clientWidth;

        // Ordered from smallest to largest breakpoint so we cascade up correctly.
        if (vw < bp.mobile_extra) {
            return sarcFirst(map, ['mobile', 'mobile_extra', 'tablet', 'tablet_extra', 'laptop', 'desktop', 'widescreen'], fallback);
        }
        if (vw < bp.tablet) {
            return sarcFirst(map, ['mobile_extra', 'mobile', 'tablet', 'tablet_extra', 'laptop', 'desktop', 'widescreen'], fallback);
        }
        if (vw < bp.tablet_extra) {
            return sarcFirst(map, ['tablet', 'mobile_extra', 'mobile', 'tablet_extra', 'laptop', 'desktop', 'widescreen'], fallback);
        }
        if (vw < bp.laptop) {
            return sarcFirst(map, ['tablet_extra', 'tablet', 'mobile_extra', 'mobile', 'laptop', 'desktop', 'widescreen'], fallback);
        }
        if (vw < 1440) {
            // laptop range
            return sarcFirst(map, ['laptop', 'desktop', 'widescreen', 'tablet_extra', 'tablet', 'mobile_extra', 'mobile'], fallback);
        }
        if (vw >= bp.widescreen) {
            return sarcFirst(map, ['widescreen', 'desktop', 'laptop', 'tablet_extra', 'tablet', 'mobile_extra', 'mobile'], fallback);
        }
        // desktop (1440 – widescreen)
        return sarcFirst(map, ['desktop', 'widescreen', 'laptop', 'tablet_extra', 'tablet', 'mobile_extra', 'mobile'], fallback);
    }

    /** Pick first non-null value from an ordered list of keys in a map. */
    function sarcFirst(map, keys, fallback) {
        for (var i = 0; i < keys.length; i++) {
            var v = map[keys[i]];
            if (v !== undefined && v !== null) { return v; }
        }
        return fallback;
    }

    /* ── Clip-Path Shape Presets ─────────────────────────────────────────── */
    function getClipShapes(config) {
        var preset = config.clipPathPreset || 'parallelogram';
        var dir = config.clipDirection || 'ltr';
        var n = config.clipIntensity || 8;
        var shapes = { 'default': 'none', active: 'none', prev: 'none', next: 'none' };

        switch (preset) {
            case 'parallelogram':
                if (dir === 'ltr') {
                    shapes['default'] = 'polygon(' + n + '% 0%,100% 0%,' + (100 - n) + '% 100%,0% 100%)';
                    shapes.active = 'polygon(0% 0%,100% 0%,100% 100%,0% 100%)';
                    shapes.prev = 'polygon(0% 0%,' + (100 - n) + '% 0%,100% 100%,' + n + '% 100%)';
                } else {
                    shapes['default'] = 'polygon(0% 0%,' + (100 - n) + '% 0%,100% 100%,' + n + '% 100%)';
                    shapes.active = 'polygon(0% 0%,100% 0%,100% 100%,0% 100%)';
                    shapes.prev = 'polygon(' + n + '% 0%,100% 0%,' + (100 - n) + '% 100%,0% 100%)';
                }
                shapes.next = shapes['default'];
                break;

            case 'diamond':
                shapes['default'] = 'polygon(50% 0%,100% 50%,50% 100%,0% 50%)';
                shapes.active = 'polygon(' + n + '% 0%,' + (100 - n) + '% 0%,100% ' + n + '%,100% ' + (100 - n) + '%,' + (100 - n) + '% 100%,' + n + '% 100%,0% ' + (100 - n) + '%,0% ' + n + '%)';
                shapes.prev = shapes['default'];
                shapes.next = shapes['default'];
                break;

            case 'arch-top':
                shapes['default'] = 'polygon(0% ' + n + '%,50% 0%,100% ' + n + '%,100% 100%,0% 100%)';
                shapes.active = 'polygon(0% 0%,100% 0%,100% 100%,0% 100%)';
                shapes.prev = shapes['default'];
                shapes.next = shapes['default'];
                break;

            case 'hexagon':
                shapes['default'] = 'polygon(' + n + '% 0%,' + (100 - n) + '% 0%,100% 50%,' + (100 - n) + '% 100%,' + n + '% 100%,0% 50%)';
                shapes.active = shapes['default'];
                shapes.prev = shapes['default'];
                shapes.next = shapes['default'];
                break;

            case 'circle-clip':
                var r = Math.max(35, Math.min(50, 40 + n * 0.5));
                shapes['default'] = 'ellipse(' + (r - 5) + '% 45% at 50% 50%)';
                shapes.active = 'ellipse(' + r + '% 50% at 50% 50%)';
                shapes.prev = shapes['default'];
                shapes.next = shapes['default'];
                break;

            case 'wave':
                shapes['default'] = 'polygon(0% 0%,100% 0%,100% ' + (100 - n) + '%,75% 100%,50% ' + (100 - n) + '%,25% 100%,0% ' + (100 - n) + '%)';
                shapes.active = 'polygon(0% 0%,100% 0%,100% 100%,0% 100%)';
                shapes.prev = shapes['default'];
                shapes.next = shapes['default'];
                break;
        }
        return shapes;
    }

    /* ── 3D Arc Layout ───────────────────────────────────────────────────── */
    function buildArcOptions(config) {
        var spv = sarcGetResponsiveValue(config.slidesPerView, 3);
        var gap = sarcGetResponsiveValue(config.spaceBetween, 24);
        var rotate = sarcGetResponsiveValue(config.arcRotate, 40);
        var depth = sarcGetResponsiveValue(config.arcDepth, 200);
        var offset = sarcGetResponsiveValue(config.arcOffset, 80);

        return {
            effect: 'coverflow',
            grabCursor: true,
            centeredSlides: true,
            slidesPerView: spv,
            spaceBetween: gap,
            loop: config.loop !== false,
            speed: config.speed || 700,
            coverflowEffect: {
                rotate: rotate,
                stretch: 0,
                depth: depth,
                modifier: 1,
                slideShadows: false,
            },
            watchSlidesProgress: true,
            breakpoints: buildSwiperBreakpoints(config, 'arc'),
            on: {
                progress: function (swiper) {
                    swiper.slides.forEach(function (slide) {
                        var p = slide.progress;
                        var ty = Math.pow(p, 2) * sarcGetResponsiveValue(config.arcOffset, 80);
                        var t = slide.style.transform || '';
                        slide.style.transform = t.replace(/\s*translateY\([^)]+\)/g, '') + ' translateY(' + ty + 'px)';
                    });
                },
                resize: function (swiper) {
                    swiper.params.slidesPerView = sarcGetResponsiveValue(config.slidesPerView, 3);
                    swiper.params.spaceBetween = sarcGetResponsiveValue(config.spaceBetween, 24);
                    swiper.params.coverflowEffect.rotate = sarcGetResponsiveValue(config.arcRotate, 40);
                    swiper.params.coverflowEffect.depth = sarcGetResponsiveValue(config.arcDepth, 200);
                    swiper.update();
                },
            },
        };
    }

    /* ── Clip-Path Layout ────────────────────────────────────────────────── */
    function buildClipOptions(config) {
        var shapes = getClipShapes(config);

        function applyClipShapes(swiper) {
            swiper.slides.forEach(function (slide) {
                var card = slide.querySelector('.sarc-card');
                if (!card) { return; }

                var customClip = slide.dataset.clip;
                if (customClip) {
                    card.style.clipPath = customClip;
                    return;
                }

                if (slide.classList.contains('swiper-slide-active')) {
                    card.style.clipPath = shapes.active;
                } else if (slide.classList.contains('swiper-slide-prev')) {
                    card.style.clipPath = shapes.prev;
                } else {
                    card.style.clipPath = shapes.next;
                }
            });
        }

        return {
            grabCursor: true,
            centeredSlides: true,
            slidesPerView: sarcGetResponsiveValue(config.slidesPerView, 3),
            spaceBetween: sarcGetResponsiveValue(config.spaceBetween, 24),
            loop: config.loop !== false,
            speed: config.speed || 700,
            watchSlidesProgress: true,
            breakpoints: buildSwiperBreakpoints(config, 'clip'),
            on: {
                slideChange: function (swiper) { applyClipShapes(swiper); },
                init: function (swiper) { applyClipShapes(swiper); },
                transitionStart: function (swiper) { applyClipShapes(swiper); },
                resize: function (swiper) {
                    swiper.params.slidesPerView = sarcGetResponsiveValue(config.slidesPerView, 3);
                    swiper.params.spaceBetween = sarcGetResponsiveValue(config.spaceBetween, 24);
                    swiper.update();
                    applyClipShapes(swiper);
                },
            },
        };
    }

    /* ── Circle Top Slide Layout ─────────────────────────────────────────── */
    function buildCircleTopOptions(config) {
        var spd = config.speed || 700;
        var ease = 'cubic-bezier(0.23,1,0.32,1)';

        function apply(swiper) {
            var rotateY = sarcGetResponsiveValue(config.circleTopRotate, 55);
            var depthMax = sarcGetResponsiveValue(config.circleTopDepth, 120);
            var scaleStep = sarcGetResponsiveValue(config.circleTopScale, 0.12);

            swiper.slides.forEach(function (slide) {
                var p = slide.progress;
                var absP = Math.abs(p);
                var scale = Math.max(0.5, 1 - absP * scaleStep);
                var rotY = p * rotateY;
                var depth = -absP * depthMax;
                var op = Math.max(0.25, 1 - absP * 0.2);

                slide.style.transition =
                    'transform ' + spd + 'ms ' + ease +
                    ', opacity ' + spd + 'ms ease';
                slide.style.transform =
                    'perspective(1400px)' +
                    ' translateZ(' + depth + 'px)' +
                    ' rotateY(' + rotY + 'deg)' +
                    ' scale(' + scale + ')';
                slide.style.opacity = op;
                slide.style.zIndex = Math.round(Math.max(0, (1 - absP) * 100));
            });
        }

        return {
            grabCursor: true,
            centeredSlides: true,
            slidesPerView: 'auto',
            loop: config.loop !== false,
            speed: spd,
            watchSlidesProgress: true,
            virtualTranslate: true,
            on: {
                progress: function (swiper) { apply(swiper); },
                setTranslate: function (swiper) { apply(swiper); },
                init: function (swiper) { apply(swiper); },
                resize: function (swiper) { apply(swiper); },
            },
        };
    }

    /* ── 3D Circle Loop Layout ───────────────────────────────────────────── */
    function buildCircleOptions(config) {
        var spd = config.speed || 900;
        var ease = 'cubic-bezier(0.23,1,0.32,1)';

        function positionCircle(swiper) {
            if (!swiper.slides.length) { return; }

            var radius = sarcGetResponsiveValue(config.circleRadius, 400);
            var tiltDeg = sarcGetResponsiveValue(config.circleTilt, 15);
            var total = swiper.slides.length;
            var tiltRad = (tiltDeg * Math.PI) / 180;
            var sw = swiper.width || 800;

            swiper.slides.forEach(function (slide) {
                var p = slide.progress;
                var fraction = p / total;
                var theta = fraction * 2 * Math.PI;

                var wx = Math.sin(theta) * radius;
                var tz = (Math.cos(theta) - 1) * radius;
                var ty = Math.sin(tiltRad) * (1 - Math.cos(theta)) * radius * 0.4;

                var naturalCenter = (slide.swiperSlideOffset || 0) + (slide.offsetWidth || 280) / 2;
                var swiperCenter = sw / 2;
                var tx = wx + (swiperCenter - naturalCenter);

                var absP = Math.abs(p);
                var opacity = Math.max(0.15, 1 - (absP / (total * 0.5)) * 0.85);
                var zIdx = Math.round(Math.max(0, (1 - absP / total) * 100));
                var rotY = -(fraction * 360);

                slide.style.transition =
                    'transform ' + spd + 'ms ' + ease +
                    ', opacity ' + spd + 'ms ease';
                slide.style.transform =
                    'translateX(' + tx + 'px)' +
                    ' translateY(' + ty + 'px)' +
                    ' translateZ(' + tz + 'px)' +
                    ' rotateY(' + rotY + 'deg)';
                slide.style.opacity = opacity;
                slide.style.zIndex = zIdx;
            });
        }

        return {
            grabCursor: true,
            centeredSlides: true,
            slidesPerView: 'auto',
            loop: config.loop !== false,
            speed: spd,
            watchSlidesProgress: true,
            virtualTranslate: true,
            on: {
                progress: function (swiper) { positionCircle(swiper); },
                setTranslate: function (swiper) { positionCircle(swiper); },
                init: function (swiper) { positionCircle(swiper); },
                resize: function (swiper) { positionCircle(swiper); },
            },
        };
    }

    /* ── Swiper Breakpoints Builder ──────────────────────────────────────── */
    /*
     * Builds a Swiper `breakpoints` object so Swiper itself also switches
     * slidesPerView and spaceBetween at the right widths.
     * This acts as a secondary mechanism alongside our resize handler.
     */
    function buildSwiperBreakpoints(config, layoutType) {
        var bp = getSarcBreakpoints();

        // For auto-slidesPerView layouts (circle, circle-top) we skip this.
        if (layoutType === 'circle' || layoutType === 'circle-top') { return {}; }

        var spvMap = config.slidesPerView || {};
        var gapMap = config.spaceBetween || {};

        function spv(device, fb) { return sarcFirst(spvMap, [device], fb); }
        function gap(device, fb) { return sarcFirst(gapMap, [device], fb); }

        var breakpoints = {};

        // mobile (0 → mobile_extra threshold): smallest — no key needed, set as default in options
        // mobile_extra
        breakpoints[bp.mobile_extra] = {
            slidesPerView: spv('mobile_extra', spv('mobile', 1)),
            spaceBetween: gap('mobile_extra', gap('mobile', 10)),
        };
        // tablet
        breakpoints[bp.tablet] = {
            slidesPerView: spv('tablet', 2),
            spaceBetween: gap('tablet', 16),
        };
        // tablet_extra
        breakpoints[bp.tablet_extra] = {
            slidesPerView: spv('tablet_extra', 2),
            spaceBetween: gap('tablet_extra', 20),
        };
        // laptop
        breakpoints[bp.laptop] = {
            slidesPerView: spv('laptop', 3),
            spaceBetween: gap('laptop', 24),
        };
        // widescreen
        breakpoints[bp.widescreen] = {
            slidesPerView: spv('widescreen', 4),
            spaceBetween: gap('widescreen', 32),
        };

        return breakpoints;
    }

    /* ── Main Init ───────────────────────────────────────────────────────── */
    function initArcCarousel($scope) {
        var container = $scope.find('.sarc-swiper')[0];
        if (!container) { return; }

        if (typeof Swiper === 'undefined') {
            console.warn('[Sifency 3D Arc] Swiper is not loaded.');
            return;
        }

        var raw = container.dataset.config || '{}';
        var config;
        try { config = JSON.parse(raw); } catch (e) { config = {}; }
        var layout = config.layout || '3d-arc';

        // Destroy previous instance (Elementor editor live reload)
        if (container.__swiperInstance) {
            container.__swiperInstance.destroy(true, true);
            container.__swiperInstance = null;
        }

        // Reset the breakpoint cache on each init so Elementor preview
        // changes are picked up immediately.
        SARC_BREAKPOINTS = null;

        var options = {};
        if (layout === '3d-arc') {
            options = buildArcOptions(config);
        } else if (layout === 'clip-path') {
            options = buildClipOptions(config);
        } else if (layout === 'circle-top') {
            options = buildCircleTopOptions(config);
        } else if (layout === '3d-circle') {
            options = buildCircleOptions(config);
        }

        // Autoplay
        if (config.autoplay) {
            options.autoplay = {
                delay: config.autoplayDelay || 3000,
                disableOnInteraction: false,
                pauseOnMouseEnter: true,
            };
        }

        // Navigation
        if (config.showNav) {
            var prevEl = $scope.find('.sarc-prev')[0];
            var nextEl = $scope.find('.sarc-next')[0];
            if (prevEl && nextEl) {
                options.navigation = { prevEl: prevEl, nextEl: nextEl };
            }
        }

        // Pagination
        if (config.showPagination) {
            var paginationEl = $scope.find('.sarc-pagination')[0];
            if (paginationEl) {
                options.pagination = { el: paginationEl, clickable: true };
            }
        }

        options.keyboard = { enabled: true };
        options.a11y = { enabled: true };

        var swiper = new Swiper(container, options);
        container.__swiperInstance = swiper;
    }

    /* ── Elementor Hook ──────────────────────────────────────────────────── */
    $(window).on('elementor/frontend/init', function () {
        elementorFrontend.hooks.addAction(
            'frontend/element_ready/sifency-3d-arc-carousel.default',
            initArcCarousel
        );
    });

})(jQuery);