/**
 * Copyright Aquera Inc 2025
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */

import type NileCarouselItem from './nile-carousel-item/nile-carousel-item';
import { clamp } from '../internal/math';
import { prefersReducedMotion } from '../internal/animate';

export function isCarouselItem(element: HTMLElement): boolean {
  return element.tagName.toLowerCase() === 'nile-carousel-item';
}

export function findMostVisibleSlide(
  slides: NileCarouselItem[],
  scrollContainer: HTMLElement
): NileCarouselItem | null {
  if (!slides.length || !scrollContainer) {
    return null;
  }

  const scrollContainerRect = scrollContainer.getBoundingClientRect();
  let mostVisibleSlide: NileCarouselItem | null = null;
  let maxVisibleArea = 0;

  slides.forEach((slide) => {
    const slideRect = slide.getBoundingClientRect();
    const visibleWidth = Math.max(
      0,
      Math.min(slideRect.right, scrollContainerRect.right) - Math.max(slideRect.left, scrollContainerRect.left)
    );

    if (visibleWidth > maxVisibleArea) {
      maxVisibleArea = visibleWidth;
      mostVisibleSlide = slide;
    }
  });

  return mostVisibleSlide;
}

export function getPageCount(slidesCount: number, slidesPerPage: number, slidesPerMove: number): number {
  const pages = (slidesCount - slidesPerPage) / slidesPerMove + 1;
  return Math.ceil(pages);
}

export function getCurrentPage(activeSlide: number, slidesPerMove: number): number {
  return Math.floor(activeSlide / slidesPerMove);
}

export function canScrollNext(currentPage: number, pageCount: number, loop: boolean = false): boolean {
  if (loop) {
    return true;
  }
  return currentPage < pageCount - 1;
}

export function canScrollPrev(currentPage: number, loop: boolean = false): boolean {
  if (loop) {
    return true;
  }
  return currentPage > 0;
}

export function shouldSnapToSlide(slideIndex: number, slidesPerMove: number): boolean {
  return (slideIndex + slidesPerMove) % slidesPerMove === 0;
}

export function calculateScrollPosition(
  slideRect: DOMRect,
  containerRect: DOMRect,
  containerScrollLeft: number,
  containerScrollTop: number
): { left: number; top: number } {
  const nextLeft = slideRect.left - containerRect.left;
  const nextTop = slideRect.top - containerRect.top;

  return {
    left: nextLeft + containerScrollLeft,
    top: nextTop + containerScrollTop
  };
}

export function scrollToSlide(
  slide: HTMLElement,
  scrollContainer: HTMLElement,
  behavior: ScrollBehavior = 'smooth',
  setPendingSlideChange: (value: boolean) => void
): void {
  setPendingSlideChange(true);
  window.requestAnimationFrame(() => {
    if (!scrollContainer) {
      return;
    }

    const scrollContainerRect = scrollContainer.getBoundingClientRect();
    const nextSlideRect = slide.getBoundingClientRect();

    const nextLeft = nextSlideRect.left - scrollContainerRect.left;
    const nextTop = nextSlideRect.top - scrollContainerRect.top;

    if (nextLeft || nextTop) {
      const scrollPosition = calculateScrollPosition(
        nextSlideRect,
        scrollContainerRect,
        scrollContainer.scrollLeft,
        scrollContainer.scrollTop
      );

      setPendingSlideChange(true);
      scrollContainer.scrollTo({
        left: scrollPosition.left,
        top: scrollPosition.top,
        behavior
      });
    } else {
      setPendingSlideChange(false);
    }
  });
}

export function goToSlide(
  index: number,
  slides: HTMLElement[],
  slidesPerPage: number,
  behavior: ScrollBehavior = 'smooth',
  loop: boolean = false
): { newActiveSlide: number; slideToScroll: HTMLElement } | null {
  if (!slides.length) {
    return null;
  }

  let newActiveSlide: number;
  let nextSlideIndex: number;

  if (loop) {
    const totalSlides = slides.length;
    nextSlideIndex = ((index % totalSlides) + totalSlides) % totalSlides;
    newActiveSlide = clamp(nextSlideIndex, 0, slides.length - slidesPerPage);
  } else {
    newActiveSlide = clamp(index, 0, slides.length - slidesPerPage);
    nextSlideIndex = clamp(index, 0, slides.length - 1);
  }

  const nextSlide = slides[nextSlideIndex];

  return {
    newActiveSlide,
    slideToScroll: nextSlide
  };
}

