<template>
  <div
    :style="`--slide-width: ${slideWidth}px`"
    class="ui-swiper"
  >
    <div
      ref="trackRef"
      class="ui-swiper__track"
      @scroll="onScroll"
    >
      <div
        v-for="(item, index) in items"
        :key="`ui-swiper-${index}`"
        class="ui-swiper__slide"
      >
        <slot v-bind="item" />
      </div>
      <div
        v-if="$slots.append"
        class="ui-swiper__slide ui-swiper__slide--append"
      >
        <slot name="append" />
      </div>
    </div>

    <button
      :class="{ '--hidden': isAtStart }"
      :aria-label="$t('previous')"
      class="ui-swiper__nav --prev"
      @click="scrollPrev"
    >
      <UiIcon
        class="ui-swiper__nav-icon"
        name="angle-left"
        size="24"
      />
    </button>

    <button
      :class="{ '--hidden': isAtEnd }"
      :aria-label="$t('next')"
      class="ui-swiper__nav --next"
      @click="scrollNext"
    >
      <UiIcon
        class="ui-swiper__nav-icon"
        name="angle-right"
        size="24"
      />
    </button>
  </div>
</template>

<script setup lang="ts">
const props = withDefaults(defineProps<{
  items: any[]
  slideWidth?: number
}>(), {
  slideWidth: 96,
});

const trackRef = ref<HTMLElement | null>(null);

const isAtStart = ref(true);
const isAtEnd = ref(true);

let scrollTimer: ReturnType<typeof setTimeout> | null = null;

function updateEdges() {
  const el = trackRef.value;
  if (!el) return;

  const { scrollLeft, scrollWidth, clientWidth } = el;
  isAtStart.value = scrollLeft <= 2;
  isAtEnd.value = scrollLeft + clientWidth >= scrollWidth - 2;
}

function onScroll() {
  if (scrollTimer) return;
  scrollTimer = setTimeout(() => {
    scrollTimer = null;
    updateEdges();
  }, 60);
}

function scrollPrev() {
  trackRef.value?.scrollBy({ left: -(props.slideWidth * 3), behavior: 'smooth' });
}

function scrollNext() {
  trackRef.value?.scrollBy({ left: props.slideWidth * 3, behavior: 'smooth' });
}

onMounted(() => {
  updateEdges();
});
</script>

<style lang="scss">
/* Запас под ring ховера карточек: у трека overflow-x: auto, а значит по спецификации
   и overflow-y становится auto — без запаса тень обрезается по всем краям. */
$ring-room: 4px;

.ui-swiper {
  position: relative;

  @include to-br(sm) {
    width: 100vw;
    margin-left: calc(-1 * var(--container-padding-inline));
  }
}

.ui-swiper__track {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-block: $ring-room;
  padding-inline: var(--container-padding-inline);

  /* Без scroll-padding mandatory-снап считает точкой привязки сам край скроллпорта
     и на старте подтягивает первую карточку под padding — она липнет к краю вьюпорта. */
  scroll-padding-inline: var(--container-padding-inline);

  &::-webkit-scrollbar {
    display: none;
  }

  @include from-br(xs) {
    gap: 8px;
  }

  /* Отрицательный margin гасит боковой запас, чтобы первая карточка осталась по краю */
  @include from-br(sm) {
    gap: 16px;
    padding-inline: $ring-room;
    scroll-padding-inline: $ring-room;
    margin-inline: -$ring-room;
  }
}

.ui-swiper__slide {
  flex-shrink: 0;
  width: var(--slide-width);
  scroll-snap-align: start;
}

$nav-gap: 4px;

.ui-swiper__nav {
  display: none;

  @include from-br(sm) {
    $size: 40px;

    position: absolute;
    top: calc(var(--slide-width) / 2 + #{$ring-room});
    transform: translateY(-50%);
    z-index: calc($z-index-plus + 2);
    display: grid;
    place-content: center;
    cursor: pointer;
    padding: 0;
    width: $size;
    height: $size;
    border-radius: 50%;
    border: $swiper-nav-border;
    background-color: $swiper-nav-bg;
    color: $swiper-nav-text;
    box-shadow: 0 4px 6px -1px $swiper-nav-shadow, 0 2px 4px -2px $swiper-nav-shadow;
    opacity: 1;
    transition: opacity var(--transition);

    @include hover(background-color) {
      background-color: $swiper-nav-bg--hover;
    }

    /* Трек вылезает за .ui-swiper на $ring-room, поэтому градиент уводим на
       ($nav-gap + $ring-room) — иначе между затемнением и краем свайпера
       остаётся полоска непокрытых карточек. */
    &::before {
      position: absolute;
      content: '';
      top: 50%;
      right: -($nav-gap + $ring-room);
      transform: translateY(-50%);
      display: block;
      width: 64px;
      height: calc(var(--slide-width) + #{$ring-room * 2});
      background: linear-gradient(270deg, $swiper-fade 0%, $swiper-fade-end 100%);
    }

    &.--hidden {
      opacity: 0;
      pointer-events: none;
    }
  }
}

.ui-swiper__nav.--prev {
  @include from-br(sm) {
    left: $nav-gap;
    transform: translateY(-50%) scaleX(-1);

    .ui-swiper__nav-icon {
      transform: scaleX(-1);
    }
  }
}

.ui-swiper__nav.--next {
  @include from-br(sm) {
    right: $nav-gap;
  }
}

.ui-swiper__nav-icon {
  position: relative;
  z-index: $z-index-plus;
}
</style>
