"use client";

import {
  useCallback,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from "react";
import {
  animate,
  motion,
  useAnimationFrame,
  useMotionValue,
} from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { ProductCard } from "@/components/product/ProductCard";
import type { Product } from "@/data/types";
import { cn } from "@/lib/cn";

const GAP = 16;
/** Continuous scroll speed (px / second). */
const SPEED_PX = 42;
const SLIDE_EASE = [0.22, 1, 0.36, 1] as const;

function useVisibleCount() {
  const [count, setCount] = useState(2);

  useEffect(() => {
    const update = () => {
      const w = window.innerWidth;
      if (w >= 640) setCount(4);
      else setCount(2);
    };
    update();
    window.addEventListener("resize", update);
    return () => window.removeEventListener("resize", update);
  }, []);

  return count;
}

/**
 * Continuous auto-sliding product strip (infinite marquee).
 * Pauses on hover; arrows give a fluid one-card nudge.
 */
export function ProductSlider({
  products,
  showProgress = false,
  autoPlay = true,
}: {
  products: Product[];
  showProgress?: boolean;
  autoPlay?: boolean;
}) {
  const visible = useVisibleCount();
  const viewportRef = useRef<HTMLDivElement>(null);
  const [viewportW, setViewportW] = useState(0);
  const [paused, setPaused] = useState(false);
  const [dragging, setDragging] = useState(false);
  const [reduceMotion, setReduceMotion] = useState(false);
  const x = useMotionValue(0);
  const stopNudge = useRef<(() => void) | null>(null);

  const canSlide = products.length > 1;

  const track = useMemo(() => {
    if (!products.length) return [];
    if (!canSlide) return products;
    return [...products, ...products, ...products];
  }, [products, canSlide]);

  const cardW =
    viewportW > 0
      ? (viewportW - GAP * Math.max(visible - 1, 0)) / visible
      : 0;
  const step = cardW > 0 ? cardW + GAP : 0;
  const loopWidth = step * products.length;

  const wrap = useCallback(
    (value: number) => {
      if (!loopWidth) return value;
      let v = value;
      // Keep inside middle copy: (-2L, -L]
      while (v <= -2 * loopWidth) v += loopWidth;
      while (v > -loopWidth) v -= loopWidth;
      return v;
    },
    [loopWidth],
  );

  useLayoutEffect(() => {
    const el = viewportRef.current;
    if (!el) return;
    const measure = () => setViewportW(el.clientWidth);
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  useEffect(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    const sync = () => setReduceMotion(mq.matches);
    sync();
    mq.addEventListener("change", sync);
    return () => mq.removeEventListener("change", sync);
  }, []);

  useEffect(() => {
    if (!canSlide || !loopWidth) {
      x.set(0);
      return;
    }
    x.set(-loopWidth);
  }, [canSlide, loopWidth, x]);

  useAnimationFrame((_, delta) => {
    if (
      !autoPlay ||
      !canSlide ||
      !loopWidth ||
      paused ||
      dragging ||
      reduceMotion
    ) {
      return;
    }
    const dt = Math.min(delta, 40) / 1000;
    x.set(wrap(x.get() - SPEED_PX * dt));
  });

  const nudge = useCallback(
    (dir: 1 | -1) => {
      if (!step || !canSlide || !loopWidth) return;
      stopNudge.current?.();
      setPaused(true);

      const from = x.get();
      const target = from - dir * step;

      const controls = animate(from, target, {
        duration: 0.8,
        ease: SLIDE_EASE,
        onUpdate: (v) => x.set(v),
        onComplete: () => {
          x.set(wrap(target));
          stopNudge.current = null;
          window.setTimeout(() => setPaused(false), 700);
        },
      });
      stopNudge.current = () => controls.stop();
    },
    [canSlide, loopWidth, step, wrap, x],
  );

  if (!products.length) return null;

  return (
    <div
      className="relative"
      onMouseEnter={() => setPaused(true)}
      onMouseLeave={() => {
        if (!dragging) setPaused(false);
      }}
    >
      <div className="mb-3 flex items-center justify-end gap-2">
        <SliderButton
          label="Previous products"
          onClick={() => nudge(-1)}
          disabled={!canSlide}
        >
          <ChevronLeft className="h-4 w-4" />
        </SliderButton>
        <SliderButton
          label="Next products"
          onClick={() => nudge(1)}
          disabled={!canSlide}
        >
          <ChevronRight className="h-4 w-4" />
        </SliderButton>
      </div>

      <div
        ref={viewportRef}
        className="overflow-hidden [mask-image:linear-gradient(to_right,transparent,black_1.5%,black_98.5%,transparent)]"
      >
        <motion.div
          className={cn(
            "flex will-change-transform",
            canSlide && "cursor-grab active:cursor-grabbing",
          )}
          style={{ x, gap: GAP, touchAction: "pan-y" }}
          drag={canSlide ? "x" : false}
          dragElastic={0.06}
          dragMomentum={false}
          onDragStart={() => {
            stopNudge.current?.();
            setDragging(true);
            setPaused(true);
          }}
          onDragEnd={() => {
            x.set(wrap(x.get()));
            setDragging(false);
            window.setTimeout(() => setPaused(false), 500);
          }}
        >
          {track.map((product, i) => (
            <div
              key={`${product.id}-${i}`}
              className="min-w-0 shrink-0"
              style={{
                width: cardW || undefined,
                flex: cardW
                  ? `0 0 ${cardW}px`
                  : `0 0 calc((100% - ${(visible - 1) * GAP}px) / ${visible})`,
              }}
            >
              <ProductCard
                product={product}
                index={i % products.length}
                compact
                showProgress={showProgress}
              />
            </div>
          ))}
        </motion.div>
      </div>
    </div>
  );
}

function SliderButton({
  children,
  label,
  onClick,
  disabled,
}: {
  children: ReactNode;
  label: string;
  onClick: () => void;
  disabled?: boolean;
}) {
  return (
    <button
      type="button"
      aria-label={label}
      onClick={onClick}
      disabled={disabled}
      className="inline-flex h-8 w-8 items-center justify-center border border-border bg-[var(--paper)] text-foreground transition hover:border-foreground disabled:pointer-events-none disabled:opacity-40"
    >
      {children}
    </button>
  );
}
