<script setup lang="ts">
import { ChevronLeft, ChevronRight } from '@lucide/vue';
import { ref, onMounted, nextTick } from 'vue';

const track = ref<HTMLElement | null>(null);
const canScrollLeft = ref(false);
const canScrollRight = ref(false);

// Drag state
const isDragging = ref(false);
const dragStartX = ref(0);
const dragScrollStart = ref(0);
const hasDragged = ref(false);
let dragRafId = 0;

const updateScrollButtons = () => {
    const el = track.value;

    if (!el) {
        return;
    }

    canScrollLeft.value = el.scrollLeft > 4;
    canScrollRight.value = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
};

const scrollTo = (direction: 'left' | 'right') => {
    const el = track.value;

    if (!el) {
        return;
    }

    const amount = el.clientWidth * 0.6;
    el.style.scrollBehavior = 'smooth';
    el.scrollBy({ left: direction === 'left' ? -amount : amount });

    requestAnimationFrame(() => {
        el.style.scrollBehavior = '';
    });
};

const onDragStart = (e: MouseEvent | TouchEvent) => {
    const el = track.value;

    if (!el) {
        return;
    }

    isDragging.value = true;
    hasDragged.value = false;
    dragStartX.value = 'touches' in e ? e.touches[0].pageX : e.pageX;
    dragScrollStart.value = el.scrollLeft;
};

const onDragMove = (e: MouseEvent | TouchEvent) => {
    if (!isDragging.value) {
        return;
    }

    e.preventDefault();

    const el = track.value;

    if (!el) {
        return;
    }

    const currentX = 'touches' in e ? e.touches[0].pageX : e.pageX;
    const delta = currentX - dragStartX.value;

    if (Math.abs(delta) > 3) {
        hasDragged.value = true;
    }

    cancelAnimationFrame(dragRafId);
    dragRafId = requestAnimationFrame(() => {
        el.scrollLeft = dragScrollStart.value - delta;
    });
};

const onDragEnd = () => {
    isDragging.value = false;
    cancelAnimationFrame(dragRafId);
    nextTick(() => updateScrollButtons());
};

const preventClick = (e: Event) => {
    if (hasDragged.value) {
        e.preventDefault();
        e.stopPropagation();
    }
};

onMounted(() => {
    nextTick(() => updateScrollButtons());
});

defineExpose({ updateScrollButtons });
</script>

<template>
    <div class="relative flex items-center min-w-0">
        <button v-show="canScrollLeft" @click="scrollTo('left')"
            class="absolute left-0 z-10 flex items-center justify-center w-7 h-7 rounded-full bg-white/90 border border-slate-200 shadow-sm hover:bg-amber-50 hover:border-amber-300 transition-all cursor-pointer -ml-1">
            <ChevronLeft class="w-4 h-4 text-slate-500" />
        </button>

        <div ref="track" @scroll="updateScrollButtons"
            @mousedown="onDragStart" @mousemove="onDragMove" @mouseup="onDragEnd" @mouseleave="onDragEnd"
            @touchstart.passive="onDragStart" @touchmove="onDragMove" @touchend="onDragEnd"
            @click.capture="preventClick"
            class="flex gap-2 overflow-x-auto scrollbar-hide px-1 py-1 select-none touch-pan-y will-change-scroll"
            :class="[
                { 'mx-8': canScrollLeft || canScrollRight },
                isDragging ? 'cursor-grabbing' : 'cursor-grab'
            ]">
            <slot />
        </div>

        <button v-show="canScrollRight" @click="scrollTo('right')"
            class="absolute right-0 z-10 flex items-center justify-center w-7 h-7 rounded-full bg-white/90 border border-slate-200 shadow-sm hover:bg-amber-50 hover:border-amber-300 transition-all cursor-pointer -mr-1">
            <ChevronRight class="w-4 h-4 text-slate-500" />
        </button>
    </div>
</template>

<style scoped>
.scrollbar-hide::-webkit-scrollbar {
    display: none;
}
.scrollbar-hide {
    -ms-overflow-style: none;
    scrollbar-width: none;
}
</style>
