import { prefersReducedMotion } from '../_utils/slider-utils.js';

function initImageSlider(root) {
    if (!root || root.dataset.imageSliderInitialized === '1') return;
    root.dataset.imageSliderInitialized = '1';

    const track = root.querySelector('[data-image-slider-track]');
    const rail = root.querySelector('[data-image-slider-rail]');
    const prev = root.querySelector('[data-image-slider-prev]');
    const next = root.querySelector('[data-image-slider-next]');
    if (!track || !rail || !prev || !next) return;

    const reduced = prefersReducedMotion();

    const BOOTSTRAP_MIN = {
        '': 0,
        'sm-': 576,
        'md-': 768,
        'lg-': 992,
        'xl-': 1200,
        'xxl-': 1400,
    };

    const clamp = (n, min, max) => Math.min(Math.max(n, min), max);
    const px = (v) => Number(String(v || '').replace('px', '')) || 0;

    const parseColumnSettings = () => {
        try {
            const parsed = JSON.parse(root.dataset.columnSettings || '[]');
            return Array.isArray(parsed) ? parsed : [];
        } catch (_) {
            return [];
        }
    };

    const spvFallback = () => clamp(Number(root.dataset.spvDefault || 3) || 3, 1, 5);

    const computeSpvFromColumns = () => {
        const settings = parseColumnSettings();
        if (!settings.length) return spvFallback();

        const w = window.innerWidth || 0;
        let bestCol = 0;
        let bestMin = -1;

        for (const row of settings) {
            const sel = row.break_selector ?? row['break selector'] ?? '';
            const col = Number(row.column_width ?? row['column width'] ?? 0);
            const min = BOOTSTRAP_MIN[String(sel)] ?? -1;

            if (min <= w && min >= bestMin && col > 0) {
                bestCol = col;
                bestMin = min;
            }
        }

        return bestCol ? clamp(Math.round(12 / bestCol), 1, 5) : spvFallback();
    };

    const applySpv = () => {
        track.style.setProperty('--spv', String(computeSpvFromColumns()));
    };

    const stopOtherVideos = (exceptEl) => {
        rail.querySelectorAll('video').forEach((v) => {
            if (v !== exceptEl && !v.paused) v.pause();
        });
    };

    let originalSlides = Array.from(rail.children);
    if (!originalSlides.length) return;

    let index = 0;        // current index in the CLONED rail
    let cloneCount = 0;   // how many slides cloned on each side
    let step = 0;         // px per slide step (slide width + gap)
    let animating = false;

    const slidesToScroll = () => Math.max(1, Number(root.dataset.scrollBy || 1) || 1);

    const measureStep = () => {
        const first = rail.querySelector('.image-slider__slide');
        if (!first) return 0;
        const styles = getComputedStyle(rail);
        const gap = px(styles.columnGap || styles.gap);
        return first.getBoundingClientRect().width + gap;
    };

    const setTransform = (x, withTransition) => {
        rail.style.transition = withTransition && !reduced ? 'transform 300ms ease' : 'none';
        rail.style.transform = `translate3d(${x}px, 0, 0)`;
    };

    const rebuildClones = () => {
        applySpv();

        // Remove old clones
        rail.querySelectorAll('[data-clone="1"]').forEach((n) => n.remove());

        originalSlides = Array.from(rail.children).filter((n) => n.dataset.clone !== '1');
        const count = originalSlides.length;
        if (!count) return;

        // Clone enough on both sides to support multi-slide scroll per click and dragging
        cloneCount = clamp(Math.max(computeSpvFromColumns(), slidesToScroll()) + 2, 3, Math.max(3, count));

        const head = [];
        const tail = [];

        for (let i = 0; i < cloneCount; i++) {
            const fromEnd = originalSlides[(count - cloneCount + i + count) % count].cloneNode(true);
            fromEnd.dataset.clone = '1';
            head.push(fromEnd);

            const fromStart = originalSlides[i % count].cloneNode(true);
            fromStart.dataset.clone = '1';
            tail.push(fromStart);
        }

        for (let i = head.length - 1; i >= 0; i--) rail.prepend(head[i]);
        tail.forEach((n) => rail.appendChild(n));

        step = measureStep();

        // Start at first ORIGINAL slide position (after left clones)
        index = cloneCount;
        setTransform(-index * step, false);
    };

    const normalizeIndexIfNeeded = () => {
        const count = originalSlides.length;
        if (!count) return;

        // If we've moved into clone area, jump (no transition) to matching original index
        if (index >= count + cloneCount) {
            index -= count;
            setTransform(-index * step, false);
        } else if (index < cloneCount) {
            index += count;
            setTransform(-index * step, false);
        }
    };

    const go = (dir) => {
        if (animating) return;
        if (!step) step = measureStep();
        if (!step) return;

        stopOtherVideos();

        animating = true;
        index += dir * slidesToScroll();
        setTransform(-index * step, true);
    };

    rail.addEventListener('transitionend', () => {
        animating = false;
        normalizeIndexIfNeeded();
    });

    prev.addEventListener('click', () => go(-1));
    next.addEventListener('click', () => go(1));

    track.addEventListener('keydown', (e) => {
        if (e.key === 'ArrowLeft') return (e.preventDefault(), go(-1));
        if (e.key === 'ArrowRight') return (e.preventDefault(), go(1));
    });

    // Video behavior: allow manual play, pause others
    rail.addEventListener(
        'play',
        (e) => {
            if (e.target && e.target.tagName === 'VIDEO') stopOtherVideos(e.target);
        },
        true
    );

    // --- Swipe/drag support (pointer events) ---
    track.style.touchAction = 'pan-y'; // allow vertical scroll on page; we handle horizontal
    track.style.userSelect = 'none';

    let dragging = false;
    let dragStartX = 0;
    let dragStartTranslate = 0;
    let lastDx = 0;

    const currentTranslate = () => {
        const t = rail.style.transform || '';
        // expected translate3d(<x>px, 0, 0)
        const m = t.match(/translate3d\((-?\d+(\.\d+)?)px/);
        return m ? Number(m[1]) : -index * (step || 0);
    };

    const isInteractiveEl = (el) => {
        if (!el) return false;
        return !!el.closest('video, a, button, input, textarea, select, label');
    };

    const snapAfterDrag = () => {
        if (!step) step = measureStep();
        if (!step) return;

        // If moved less than ~15% of a slide, snap back; otherwise advance based on distance.
        const movedSlides = -lastDx / step; // dragging left => dx negative => movedSlides positive
        const minMove = 0.15;

        let delta = 0;
        if (Math.abs(movedSlides) >= minMove) {
            delta = Math.round(movedSlides);
            if (delta === 0) delta = movedSlides > 0 ? 1 : -1;
        }

        // Don’t allow drag to move 0 if they did a meaningful swipe
        index += delta;

        animating = true;
        setTransform(-index * step, true);
    };

    const onPointerDown = (e) => {
        if (e.button !== undefined && e.button !== 0) return; // left mouse only
        if (animating) return;

        // let users click/play video controls without dragging
        if (isInteractiveEl(e.target)) return;

        if (!step) step = measureStep();
        if (!step) return;

        dragging = true;
        lastDx = 0;
        dragStartX = e.clientX;
        dragStartTranslate = currentTranslate();

        rail.style.transition = 'none';
        track.setPointerCapture?.(e.pointerId);
    };

    const onPointerMove = (e) => {
        if (!dragging) return;
        const dx = e.clientX - dragStartX;
        lastDx = dx;

        // During drag, allow wrap by normalizing index if we drift into clone zones.
        // We do it by checking the implied index from translate.
        const x = dragStartTranslate + dx;
        rail.style.transform = `translate3d(${x}px, 0, 0)`;

        // Light normalization mid-drag (prevents reaching rail extremes)
        const impliedIndex = -x / step;
        const count = originalSlides.length;

        if (count && (impliedIndex < cloneCount - 1 || impliedIndex > cloneCount + count + 1)) {
            // Convert implied index back into the safe band
            const safe = cloneCount + ((impliedIndex - cloneCount) % count + count) % count;
            index = Math.round(safe);
            dragStartTranslate = -index * step;
            dragStartX = e.clientX;
            lastDx = 0;
            rail.style.transform = `translate3d(${dragStartTranslate}px, 0, 0)`;
        }
    };

    const endDrag = () => {
        if (!dragging) return;
        dragging = false;
        snapAfterDrag();
    };

    track.addEventListener('pointerdown', onPointerDown);
    track.addEventListener('pointermove', onPointerMove);
    track.addEventListener('pointerup', endDrag);
    track.addEventListener('pointercancel', endDrag);
    track.addEventListener('pointerleave', endDrag);

    // If user starts playing a video, cancel drag state
    rail.addEventListener(
        'pointerdown',
        (e) => {
            if (e.target && e.target.closest('video')) dragging = false;
        },
        true
    );

    rebuildClones();

    let t = 0;
    window.addEventListener('resize', () => {
        clearTimeout(t);
        t = setTimeout(rebuildClones, 150);
    });
}

function initAllImageSliders() {
    document.querySelectorAll('[data-image-slider="1"]').forEach(initImageSlider);
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initAllImageSliders);
} else {
    initAllImageSliders();
}