"use client";

import { cn } from "@/app/lib/cn";
import useEmblaCarousel from "embla-carousel-react";
import { ArrowLeft, ArrowRight } from "iconsax-reactjs";
import {
  createContext,
  useContext,
  useEffect,
  useState,
  type FC,
  type HTMLAttributes,
  type ReactNode,
} from "react";

type CarouselApi = {
  scrollPrev: () => void;
  scrollNext: () => void;
  canScrollPrev: boolean;
  canScrollNext: boolean;
  slidesPerView: number;
  gap: number;
};

type ViewportRef = (node: HTMLElement | null) => void;

const CarouselContext = createContext<CarouselApi | null>(null);
const CarouselViewportContext = createContext<ViewportRef | null>(null);

function useCarousel() {
  const context = useContext(CarouselContext);
  if (!context) {
    throw new Error("Carousel components must be used within <Carousel>");
  }
  return context;
}

type ResponsiveNumber = number | { base: number; md?: number; lg?: number };

function useResponsiveNumber(value: ResponsiveNumber): number {
  const base = typeof value === "number" ? value : value.base;
  const md = typeof value === "number" ? undefined : value.md;
  const lg = typeof value === "number" ? undefined : value.lg;

  const resolve = () => {
    if (typeof window === "undefined") return base;
    if (window.matchMedia("(min-width: 1024px)").matches) return lg ?? md ?? base;
    if (window.matchMedia("(min-width: 768px)").matches) return md ?? base;
    return base;
  };

  const [resolved, setResolved] = useState(base);

  useEffect(() => {
    const update = () => setResolved(resolve());
    update();
    window.addEventListener("resize", update);
    return () => window.removeEventListener("resize", update);
  }, [base, md, lg]);

  return resolved;
}

type CarouselProps = {
  children: ReactNode;
  slidesPerView?: ResponsiveNumber;
  slidesToScroll?: number;
  gap?: ResponsiveNumber;
  direction?: "rtl" | "ltr";
  className?: string;
};

const Carousel: FC<CarouselProps> = ({
  children,
  slidesPerView = 5,
  slidesToScroll,
  gap = 40,
  direction = "rtl",
  className,
}) => {
  const resolvedSlidesPerView = useResponsiveNumber(slidesPerView);
  const resolvedGap = useResponsiveNumber(gap);
  const scrollBy = slidesToScroll ?? Math.max(1, Math.floor(resolvedSlidesPerView));

  const [emblaRef, emblaApi] = useEmblaCarousel({
    align: "start",
    direction,
    slidesToScroll: scrollBy,
    containScroll: "trimSnaps",
  });

  const [canScrollPrev, setCanScrollPrev] = useState(false);
  const [canScrollNext, setCanScrollNext] = useState(false);

  useEffect(() => {
    if (!emblaApi) return;

    const updateButtons = () => {
      setCanScrollPrev(emblaApi.canScrollPrev());
      setCanScrollNext(emblaApi.canScrollNext());
    };

    updateButtons();
    emblaApi.on("select", updateButtons);
    emblaApi.on("reInit", updateButtons);

    return () => {
      emblaApi.off("select", updateButtons);
      emblaApi.off("reInit", updateButtons);
    };
  }, [emblaApi]);

  useEffect(() => {
    emblaApi?.reInit({ slidesToScroll: scrollBy });
  }, [emblaApi, scrollBy, resolvedSlidesPerView, resolvedGap]);

  return (
    <CarouselContext.Provider
      value={{
        scrollPrev: () => emblaApi?.scrollPrev(),
        scrollNext: () => emblaApi?.scrollNext(),
        canScrollPrev,
        canScrollNext,
        slidesPerView: resolvedSlidesPerView,
        gap: resolvedGap,
      }}
    >
      <CarouselViewportContext.Provider value={emblaRef}>
        <div className={className}>{children}</div>
      </CarouselViewportContext.Provider>
    </CarouselContext.Provider>
  );
};

type CarouselControlsProps = {
  className?: string;
  prevLabel?: string;
  nextLabel?: string;
};

const CarouselControls: FC<CarouselControlsProps> = ({
  className,
  prevLabel = "قبلی",
  nextLabel = "بعدی",
}) => {
  const { scrollPrev, scrollNext, canScrollPrev, canScrollNext } = useCarousel();

  return (
    <div className={cn("flex gap-2", className)}>
      <button
        type="button"
        aria-label={prevLabel}
        disabled={!canScrollPrev}
        onClick={scrollPrev}
        className="size-8 bg-primary rounded-full flex justify-center items-center disabled:opacity-40 disabled:cursor-not-allowed transition-opacity"
      >
        <ArrowRight size={20} color="white" />
      </button>
      <button
        type="button"
        aria-label={nextLabel}
        disabled={!canScrollNext}
        onClick={scrollNext}
        className="size-8 bg-primary rounded-full flex justify-center items-center disabled:opacity-40 disabled:cursor-not-allowed transition-opacity"
      >
        <ArrowLeft size={20} color="white" />
      </button>
    </div>
  );
};

type CarouselTrackProps = {
  children: ReactNode;
  className?: string;
};

const CarouselTrack: FC<CarouselTrackProps> = ({ children, className }) => {
  const emblaRef = useContext(CarouselViewportContext);
  const { gap } = useCarousel();

  if (!emblaRef) {
    throw new Error("CarouselTrack must be used within <Carousel>");
  }

  return (
    <div className={cn("overflow-hidden", className)} ref={emblaRef}>
      <div className="flex touch-pan-y" style={{ gap }}>
        {children}
      </div>
    </div>
  );
};

type CarouselSlideProps = HTMLAttributes<HTMLDivElement> & {
  children: ReactNode;
};

const CarouselSlide: FC<CarouselSlideProps> = ({ children, className, style, ...rest }) => {
  const { slidesPerView, gap } = useCarousel();
  const totalGap = (slidesPerView - 1) * gap;

  return (
    <div
      className={cn("min-w-0 shrink-0 grow-0", className)}
      style={{
        flexBasis: `calc((100% - ${totalGap}px) / ${slidesPerView})`,
        ...style,
      }}
      {...rest}
    >
      {children}
    </div>
  );
};

export { Carousel, CarouselControls, CarouselSlide, CarouselTrack };
export default Carousel;
