"use client";

import { ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react";

interface ResponsiveBreakpoint {
  items: number;
}

interface OwlCarouselProps {
  children: ReactNode[];
  responsive: { [minWidth: number]: ResponsiveBreakpoint };
  loop?: boolean;
  margin?: number;
  nav?: boolean;
  dots?: boolean;
  autoplay?: number;
  smartSpeed?: number;
  navText?: [string, string];
  className?: string;
  marquee?: boolean;
}

export default function OwlCarousel({
  children,
  responsive,
  loop = true,
  margin = 0,
  nav = false,
  dots = true,
  autoplay = 6000,
  smartSpeed = 700,
  navText = [
    '<span class="fa fa-angle-left left"><p>Prev</p></span>',
    '<span class="fa fa-angle-right right"><p>Next</p></span>',
  ],
  className = "",
  marquee = false,
}: OwlCarouselProps) {
  const [pos, setPos] = useState(0);
  const [itemWidth, setItemWidth] = useState(0);
  const [itemsPerView, setItemsPerView] = useState(1);
  const [noTransition, setNoTransition] = useState(false);
  const outerRef = useRef<HTMLDivElement>(null);
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);

  const total = children.length;
  const perView = Math.max(1, itemsPerView);
  const cloneCount = loop ? perView : 0;
  const maxPos = marquee ? total : loop ? cloneCount + 2 * total - 1 : Math.max(0, total - perView);

  const getItems = () => {
    if (marquee) {
      return [...children, ...children].map((el, i) => ({ el, idx: i % total }));
    }
    if (!loop) return children.map((c, i) => ({ el: c, idx: i }));
    const start = children.slice(total - cloneCount);
    const end = children.slice(0, cloneCount);
    return [...start, ...children, ...end].map((el, i) => ({
      el,
      idx: (i - cloneCount + total) % total,
    }));
  };

  const items = getItems();

  useLayoutEffect(() => {
    const measure = () => {
      if (!outerRef.current) return;
      const w = outerRef.current.clientWidth;
      const pv = resolveItems(window.innerWidth);
      const iw = (w - margin * (pv - 1)) / pv;
      setItemWidth(iw);
      setItemsPerView(pv);
    };
    measure();
    const ro = new ResizeObserver(measure);
    if (outerRef.current) ro.observe(outerRef.current);
    window.addEventListener("resize", measure);
    return () => {
      ro.disconnect();
      window.removeEventListener("resize", measure);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [responsive, margin]);

  useLayoutEffect(() => {
    if (loop && !marquee) setPos(cloneCount);
  }, [cloneCount, loop, marquee]);

  const resolveItems = (width: number) => {
    const bp = Object.keys(responsive)
      .map(Number)
      .sort((a, b) => b - a);
    for (const min of bp) {
      if (width >= min) {
        return responsive[min].items;
      }
    }
    return 1;
  };

  useEffect(() => {
    if (autoplay <= 0) return;
    timerRef.current = setInterval(() => {
      if (marquee) {
        setPos((p) => {
          const next = (p + 1) % (total + 1);
          if (next === 0) {
            setNoTransition(true);
            setTimeout(() => setNoTransition(false), 60);
          }
          return next;
        });
      } else if (loop) {
        setPos((p) => {
          if (p >= maxPos) {
            setNoTransition(true);
            setTimeout(() => setNoTransition(false), 60);
            return cloneCount;
          }
          return p + 1;
        });
      } else {
        setPos((p) => Math.min(p + 1, maxPos));
      }
    }, autoplay);
    return () => {
      if (timerRef.current) clearInterval(timerRef.current);
    };
  }, [autoplay, loop, total, perView, marquee, maxPos, cloneCount]);

  const go = (dir: number) => {
    setPos((p) => {
      if (marquee) {
        const next = (p + dir + total + 1) % (total + 1);
        if (next === 0) {
          setNoTransition(true);
          setTimeout(() => setNoTransition(false), 60);
        }
        return next;
      }
      if (!loop) return Math.min(Math.max(p + dir, 0), maxPos);
      let next = p + dir;
      if (next < cloneCount) next = maxPos;
      else if (next > maxPos) next = cloneCount;
      if (next !== p + dir) {
        setNoTransition(true);
        setTimeout(() => setNoTransition(false), 60);
      }
      return next;
    });
  };

  const translateX = -pos * (itemWidth + margin);
  const activeIndex = marquee ? pos % total : loop ? (pos - cloneCount) % total : pos;
  const viewportIndex = marquee ? -1 : pos;
  const stageWidth = items.length * (itemWidth + margin);

  const getDots = () => {
    if (!dots) return [];
    return Array.from({ length: total }, (_, i) => i);
  };

  return (
    <div className={`owl-carousel owl-theme owl-loaded owl-drag${className ? ` ${className}` : ""}`}>
      <div className="owl-stage-outer" ref={outerRef}>
        <div
          className="owl-stage"
          style={{
            width: itemWidth ? stageWidth : undefined,
            transform: `translate3d(${translateX}px, 0px, 0px)`,
            transition: noTransition ? "none" : `transform ${smartSpeed}ms ease`,
            position: "relative",
          }}
        >
          {items.map((it, i) => (
            <div
              key={i}
              className={`owl-item${i === viewportIndex ? " active" : ""}`}
              style={{
                width: itemWidth || undefined,
                marginRight: margin || undefined,
                float: "left",
              }}
            >
              {it.el}
            </div>
          ))}
        </div>
      </div>
      {nav && (
        <div className="owl-nav">
          <button
            type="button"
            role="presentation"
            className="owl-prev"
            onClick={() => go(-1)}
            dangerouslySetInnerHTML={{ __html: navText[0] }}
          />
          <button
            type="button"
            role="presentation"
            className="owl-next"
            onClick={() => go(1)}
            dangerouslySetInnerHTML={{ __html: navText[1] }}
          />
        </div>
      )}
      {dots && (
        <div className="owl-dots">
          {getDots().map((i) => (
            <button
              key={i}
              type="button"
              role="button"
              className={`owl-dot${i === activeIndex ? " active" : ""}`}
              onClick={() => setPos(marquee ? i : loop ? cloneCount + i : i)}
            >
              <span />
            </button>
          ))}
        </div>
      )}
    </div>
  );
}
