const SPACE_BETWEEN_SLIDER = 100;
const SPACE_SLIDER_DURATION = 1000;
const MAX_SPEED = 20;
const SLOWDOWN_RANGE = 200;
const MAX_CHANGE_SPEED = 1.2;
const FRAME_DURATION = 1000 / 60;

const displayVariables = (
  data: { status: string; title: string }[],
  slides: { status: string; title: string }[],
): void => {
  const variablesElement = document.querySelector("#params");
  const slidesElement = document.querySelector("#slides");
  if (!variablesElement || !slidesElement) return;

  variablesElement.innerHTML = data
    .map(
      ({ status, title }) => `
          <div class="timeline3d__variable">
            <span>${title}</span>
            <span>${status}</span>
          </div>`,
    )
    .join("");

  if (slides) {
    slidesElement.innerHTML = slides
      .map(
        ({ status, title }) => `
          <div class="timeline3d__variable-small">
            <span>${title}</span>
            <span>${status}</span>
          </div>`,
      )
      .join("");
  }
};

export const runScript = (): void => {
  const container = document.getElementById("3dTimeline") as HTMLElement | null;
  if (!container) return;

  const raw = container.dataset.slider3d;
  const sliderData = raw ? JSON.parse(raw) : null;

  if (!sliderData) return;

  container.innerHTML = `
    <div class="timeline3d">
      <div class="timeline3d__category"></div>
      <div class="timeline3d__slider">

      </div>
      <div class="scrollbar" id="timeline3d-scrollbar">
        <div class="scrollbar__thumb" id="timeline3d-scrollbar-thumb"></div>
      </div>
      <div class="timeline3d__variables" id="params">

      </div>
      <div class="timeline3d__variables" id="slides">

      </div>
    </div>
  `;

  const track = container.querySelector<HTMLDivElement>(
    "#timeline3d-scrollbar",
  )!;
  const thumb = container.querySelector<HTMLDivElement>(
    "#timeline3d-scrollbar-thumb",
  )!;
  const root = container.querySelector<HTMLDivElement>(".timeline3d")!;

  const clamp = (val: number, min: number, max: number) =>
    Math.min(Math.max(val, min), max);

  let thumbX = 0;
  let startX = 0;
  let startOffset = 0;
  let activeId: number | null = null;
  let targetStatus = 0;
  const sliderWidth = sliderData.length * SPACE_BETWEEN_SLIDER;
  const slidesPerView = Math.round(
    SPACE_SLIDER_DURATION / SPACE_BETWEEN_SLIDER,
  );
  let sliderScrollStatus = 0;
  let status = 0;
  let speed = 0;
  let isAnimated = false;
  let lastFrameTime = 0;
  let activePosition = 0;
  let endPosition = 0;
  let visibleSlides;

  const startAnimated = () => {
    if (!isAnimated) {
      isAnimated = true;
      lastFrameTime = 0;
      requestAnimationFrame(animate);
    }
  };

  const render = (): void => {
    const maxOffset = track.clientWidth - thumb.clientWidth;
    sliderScrollStatus = maxOffset ? (thumbX / maxOffset) * 100 : 0;
    targetStatus = (sliderWidth / 100) * sliderScrollStatus;

    const distance = targetStatus - status;
    const absDistance = Math.abs(distance);

    let desiredSpeed = 0;
    if (absDistance > 0) {
      desiredSpeed = Math.sign(distance) * MAX_SPEED;
      if (absDistance < SLOWDOWN_RANGE) {
        desiredSpeed *= absDistance / SLOWDOWN_RANGE;
      }
    }

    const deltaSpeed = clamp(
      desiredSpeed - speed,
      -MAX_CHANGE_SPEED,
      MAX_CHANGE_SPEED,
    );
    speed += deltaSpeed;

    status = clamp(status + speed, 0, sliderWidth);

    sliderScrollStatus = (status / sliderWidth) * 100;
    root.style.setProperty(
      "--thumb-x",
      `${(sliderScrollStatus / 100) * maxOffset}px`,
    );

    if (absDistance < 0.5 && Math.abs(speed) < 0.05) {
      status = targetStatus;
      speed = 0;
    }

    activePosition = Math.round(status / SPACE_BETWEEN_SLIDER);
    endPosition =
      activePosition + Math.round(SPACE_SLIDER_DURATION / SPACE_BETWEEN_SLIDER);

    visibleSlides = [];
    for (let i = activePosition; i <= endPosition; i += 1) {
      const start = (i - slidesPerView) * SPACE_BETWEEN_SLIDER;

      const rawProgress = (status - start) / SPACE_SLIDER_DURATION;

      if (rawProgress > 1 || rawProgress < 0) continue;
      const progress = Math.round(rawProgress * 1000) / 1000;

      visibleSlides.push({ progress, title: `slide_${i}` });
    }

    const result = visibleSlides.map(({ progress, title }) => ({
      status: `${Math.round(progress * 100)}%`,
      title,
    }));

    displayVariables(
      [
        { title: "targetStatus", status: `${targetStatus.toFixed(2)}px` },
        { title: "realStatus", status: `${status.toFixed(2)}px` },
        { title: "speed", status: `${speed.toFixed(2)}px` },
        {
          title: "sliderScrollStatus",
          status: `${sliderScrollStatus.toFixed(2)}%`,
        },
        { title: "activePosition", status: `${activePosition}` },
        { title: "endPosition", status: `${endPosition}` },
      ],
      result,
    );
  };

  const animate = (time: number) => {
    if (time - lastFrameTime >= FRAME_DURATION) {
      lastFrameTime = time;
      render();
    }

    if (Math.abs(targetStatus - status) > 0.01 || Math.abs(speed) > 0.01) {
      requestAnimationFrame(animate);
    } else {
      speed = 0;
      isAnimated = false;
    }
  };

  const onPointerMove = (e: PointerEvent) => {
    const dx = e.clientX - startX;
    const maxOffset = track.clientWidth - thumb.clientWidth;
    thumbX = clamp(startOffset + dx, 0, maxOffset);
    startAnimated();
  };

  const onPointerUp = () => {
    document.removeEventListener("pointermove", onPointerMove);
    document.removeEventListener("pointerup", onPointerUp);
    if (activeId !== null) {
      thumb.releasePointerCapture(activeId);
      activeId = null;
    }
  };

  thumb.addEventListener("pointerdown", (e: PointerEvent) => {
    activeId = e.pointerId;
    thumb.setPointerCapture(activeId);

    startX = e.clientX;
    startOffset = thumbX;
    document.addEventListener("pointermove", onPointerMove);
    document.addEventListener("pointerup", onPointerUp);
  });

  startAnimated();
};
3D TIMELINE
Высчитали активный слайд, добавили параметр SPACE_SLIDER_DURATION длину прокрутки слайдера, условно слайдер будет анимироваться от 0 до 1000 или от 100 до 1100 или от 200 до 1200 и тд, высчитали прогресс для каждого слайда.