'use client';

import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import Link from 'next/link';

export type HeroSlide = {
  id: string;
  imageUrl: string;
  title?: string;
  linkUrl?: string;
};

type Props = {
  slides: HeroSlide[];
};

/** Visible slides at once — current + half of next (Dream11 peek). */
const VISIBLE = 1.5;
const GAP_PX = 10;
const TRANSITION_MS = 380;

type TrackSlide = HeroSlide & { key: string };

/**
 * Admin hero banners: ~1.5 cards visible, infinite loop, swipe + autoplay + dots.
 */
export function HeroSlider({ slides }: Props) {
  const items = slides.filter((s) => s?.imageUrl);
  const n = items.length;
  const loop = n > 1;

  // [clone(last), ...items, clone(first)] so last↔first animates seamlessly
  const track: TrackSlide[] = loop
    ? [
        { ...items[n - 1], key: `${items[n - 1].id}__clone-start` },
        ...items.map((s) => ({ ...s, key: s.id })),
        { ...items[0], key: `${items[0].id}__clone-end` },
      ]
    : items.map((s) => ({ ...s, key: s.id }));

  const viewportRef = useRef<HTMLDivElement>(null);
  /** Position in `track` (1 = first real slide when looping). */
  const [pos, setPos] = useState(loop ? 1 : 0);
  const [slideW, setSlideW] = useState(0);
  const [dragging, setDragging] = useState(false);
  const [dragPx, setDragPx] = useState(0);
  const [instant, setInstant] = useState(false);
  const startX = useRef(0);
  const startY = useRef(0);
  const axis = useRef<'x' | 'y' | null>(null);
  const paused = useRef(false);
  const moved = useRef(false);
  const posRef = useRef(pos);
  posRef.current = pos;

  const realIndex = loop ? (pos - 1 + n) % n : pos;

  const measure = useCallback(() => {
    const el = viewportRef.current;
    if (!el) return;
    const w = el.clientWidth;
    if (n <= 1) {
      setSlideW(w);
      return;
    }
    setSlideW(Math.max(0, (w - GAP_PX) / VISIBLE));
  }, [n]);

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

  useEffect(() => {
    setPos(loop ? 1 : 0);
    setInstant(false);
  }, [n, loop]);

  // After clone snap, re-enable transition on next frame
  useLayoutEffect(() => {
    if (!instant) return;
    const id = requestAnimationFrame(() => {
      requestAnimationFrame(() => setInstant(false));
    });
    return () => cancelAnimationFrame(id);
  }, [instant, pos]);

  const goReal = useCallback(
    (real: number) => {
      if (n < 1) return;
      const r = ((real % n) + n) % n;
      setPos(loop ? r + 1 : r);
    },
    [n, loop]
  );

  const stepBy = useCallback(
    (dir: 1 | -1) => {
      if (!loop) {
        setPos((p) => Math.min(n - 1, Math.max(0, p + dir)));
        return;
      }
      setPos((p) => p + dir);
    },
    [loop, n]
  );

  useEffect(() => {
    if (!loop) return;
    const t = setInterval(() => {
      if (paused.current || dragging) return;
      setPos((p) => p + 1);
    }, 4000);
    return () => clearInterval(t);
  }, [loop, dragging]);

  const step = slideW + GAP_PX;

  const snapFromClone = () => {
    if (!loop) return;
    const p = posRef.current;
    if (p === 0) {
      setInstant(true);
      setPos(n);
    } else if (p === n + 1) {
      setInstant(true);
      setPos(1);
    }
  };

  const onPointerDown = (e: React.PointerEvent) => {
    if (n < 2 || !slideW) return;
    paused.current = true;
    moved.current = false;
    setDragging(true);
    startX.current = e.clientX;
    startY.current = e.clientY;
    axis.current = null;
    setDragPx(0);
    viewportRef.current?.setPointerCapture(e.pointerId);
  };

  const onPointerMove = (e: React.PointerEvent) => {
    if (!dragging) return;
    const dx = e.clientX - startX.current;
    const dy = e.clientY - startY.current;
    if (!axis.current) {
      if (Math.abs(dx) < 6 && Math.abs(dy) < 6) return;
      axis.current = Math.abs(dx) > Math.abs(dy) ? 'x' : 'y';
    }
    if (axis.current !== 'x') return;
    e.preventDefault();
    if (Math.abs(dx) > 8) moved.current = true;
    setDragPx(dx);
  };

  const endDrag = (e: React.PointerEvent) => {
    if (!dragging) return;
    setDragging(false);
    viewportRef.current?.releasePointerCapture(e.pointerId);
    const threshold = Math.min(64, slideW * 0.2);
    if (axis.current === 'x' && Math.abs(dragPx) > threshold) {
      stepBy(dragPx < 0 ? 1 : -1);
    }
    setDragPx(0);
    axis.current = null;
    window.setTimeout(() => {
      paused.current = false;
    }, 450);
  };

  if (n === 0) return null;

  const offset = pos * step - (dragging ? dragPx : 0);

  return (
    <div
      className="relative w-full"
      onMouseEnter={() => {
        paused.current = true;
      }}
      onMouseLeave={() => {
        paused.current = false;
      }}
    >
      <div
        ref={viewportRef}
        className="relative w-full touch-pan-y select-none overflow-hidden"
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={endDrag}
        onPointerCancel={endDrag}
        role="region"
        aria-roledescription="carousel"
        aria-label="Promotions"
      >
        <div
          className="flex"
          style={{
            gap: GAP_PX,
            transform: slideW ? `translateX(-${offset}px)` : undefined,
            transition:
              dragging || !slideW || instant
                ? 'none'
                : `transform ${TRANSITION_MS}ms cubic-bezier(0.22, 1, 0.36, 1)`,
          }}
          onTransitionEnd={(e) => {
            if (e.propertyName !== 'transform') return;
            snapFromClone();
          }}
        >
          {track.map((slide, i) => {
            const media = (
              <>
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  src={slide.imageUrl}
                  alt={slide.title || `Banner ${(i % n) + 1}`}
                  className="h-full w-full object-cover"
                  draggable={false}
                />
              </>
            );

            const style: React.CSSProperties = {
              width: slideW || '66.666%',
              minWidth: slideW || '66.666%',
            };

            const className =
              'relative aspect-[2.15/1] shrink-0 overflow-hidden rounded-xl bg-d11-navy shadow-card ring-1 ring-black/5 sm:rounded-2xl';

            if (slide.linkUrl) {
              return (
                <Link
                  key={slide.key}
                  href={slide.linkUrl}
                  className={className}
                  style={style}
                  tabIndex={i === pos ? 0 : -1}
                  draggable={false}
                  onClick={(e) => {
                    if (moved.current) e.preventDefault();
                  }}
                >
                  {media}
                </Link>
              );
            }

            return (
              <div key={slide.key} className={className} style={style} aria-hidden={i !== pos}>
                {media}
              </div>
            );
          })}
        </div>
      </div>

      {n > 1 && (
        <div className="mt-2.5 flex justify-center gap-1.5">
          {items.map((s, i) => (
            <button
              key={s.id}
              type="button"
              aria-label={`Go to slide ${i + 1}`}
              aria-current={i === realIndex}
              onClick={() => {
                paused.current = true;
                goReal(i);
                window.setTimeout(() => {
                  paused.current = false;
                }, 800);
              }}
              className={`h-1.5 rounded-full transition-all ${
                i === realIndex ? 'w-4 bg-d11-red' : 'w-1.5 bg-gray-300 hover:bg-gray-400'
              }`}
            />
          ))}
        </div>
      )}
    </div>
  );
}
