import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-react'
import * as React from 'react'

import { cn } from './utils'

type CarouselOrientation = 'horizontal' | 'vertical'

interface CarouselContextValue {
  orientation: CarouselOrientation
  canScrollPrev: boolean
  canScrollNext: boolean
  scrollPrev: () => void
  scrollNext: () => void
  currentIndex: number
  setSlideCount: (count: number) => void
}

const CarouselContext = React.createContext<CarouselContextValue | null>(null)

function useCarousel() {
  const context = React.useContext(CarouselContext)
  if (!context) {
    throw new Error('useCarousel must be used within a <Carousel />')
  }
  return context
}

interface CarouselProps extends React.ComponentProps<'div'> {
  orientation?: CarouselOrientation
  loop?: boolean
  autoplay?: boolean
  autoplayInterval?: number
}

function Carousel({
  className,
  orientation = 'horizontal',
  loop = false,
  autoplay = false,
  autoplayInterval = 5000,
  children,
  ...props
}: CarouselProps) {
  const [currentIndex, setCurrentIndex] = React.useState(0)
  const [totalSlides, setTotalSlides] = React.useState(0)

  const canScrollPrev = loop || currentIndex > 0
  const canScrollNext = loop || currentIndex < totalSlides - 1

  const scrollPrev = React.useCallback(() => {
    setCurrentIndex(prev => {
      if (prev > 0) return prev - 1
      if (loop && totalSlides > 0) return totalSlides - 1
      return prev
    })
  }, [loop, totalSlides])

  const scrollNext = React.useCallback(() => {
    setCurrentIndex(prev => {
      if (prev < totalSlides - 1) return prev + 1
      if (loop) return 0
      return prev
    })
  }, [loop, totalSlides])

  const setSlideCount = React.useCallback((count: number) => {
    setTotalSlides(count)
  }, [])

  // Autoplay
  React.useEffect(() => {
    if (!autoplay || totalSlides <= 1) return

    const timer = setInterval(() => {
      scrollNext()
    }, autoplayInterval)

    return () => clearInterval(timer)
  }, [autoplay, autoplayInterval, totalSlides, scrollNext])

  // Keyboard navigation
  const handleKeyDown = React.useCallback(
    (e: React.KeyboardEvent) => {
      const prevKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp'
      const nextKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown'

      if (e.key === prevKey) {
        e.preventDefault()
        scrollPrev()
      } else if (e.key === nextKey) {
        e.preventDefault()
        scrollNext()
      }
    },
    [orientation, scrollPrev, scrollNext]
  )

  const contextValue = React.useMemo(
    () => ({
      orientation,
      canScrollPrev,
      canScrollNext,
      scrollPrev,
      scrollNext,
      currentIndex,
      setSlideCount,
    }),
    [orientation, canScrollPrev, canScrollNext, scrollPrev, scrollNext, currentIndex, setSlideCount]
  )

  return (
    <CarouselContext.Provider value={contextValue}>
      <div
        data-slot="carousel"
        role="region"
        aria-roledescription="carousel"
        aria-label="Content carousel"
        tabIndex={0}
        onKeyDown={handleKeyDown}
        className={cn('relative', className)}
        {...props}
      >
        {children}
      </div>
    </CarouselContext.Provider>
  )
}

function CarouselContent({ className, children, ...props }: React.ComponentProps<'div'>) {
  const { orientation, currentIndex, setSlideCount } = useCarousel()
  const childCount = React.Children.count(children)

  React.useEffect(() => {
    setSlideCount(childCount)
  }, [childCount, setSlideCount])

  return (
    <div data-slot="carousel-content" className={cn('overflow-hidden', className)} {...props}>
      <div
        className={cn(
          'flex transition-transform duration-300 ease-in-out',
          orientation === 'vertical' && 'flex-col'
        )}
        style={{
          transform:
            orientation === 'horizontal'
              ? `translateX(-${String(currentIndex * 100)}%)`
              : `translateY(-${String(currentIndex * 100)}%)`,
        }}
      >
        {children}
      </div>
    </div>
  )
}

function CarouselItem({ className, ...props }: React.ComponentProps<'div'>) {
  return (
    <div
      data-slot="carousel-item"
      role="group"
      aria-roledescription="slide"
      className={cn('min-w-0 shrink-0 grow-0 basis-full', className)}
      {...props}
    />
  )
}

function CarouselPrevious({ className, ...props }: React.ComponentProps<'button'>) {
  const { canScrollPrev, scrollPrev, orientation } = useCarousel()

  return (
    <button
      data-slot="carousel-previous"
      type="button"
      disabled={!canScrollPrev}
      onClick={scrollPrev}
      className={cn(
        'absolute z-10 flex size-8 items-center justify-center rounded-full border border-border-glass bg-background/80 shadow-sm backdrop-blur-sm transition-all',
        'hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50',
        orientation === 'horizontal'
          ? '-left-12 top-1/2 -translate-y-1/2'
          : '-top-12 left-1/2 -translate-x-1/2 rotate-90',
        className
      )}
      {...props}
    >
      <ArrowLeftIcon className="size-4" />
      <span className="sr-only">Previous slide</span>
    </button>
  )
}

function CarouselNext({ className, ...props }: React.ComponentProps<'button'>) {
  const { canScrollNext, scrollNext, orientation } = useCarousel()

  return (
    <button
      data-slot="carousel-next"
      type="button"
      disabled={!canScrollNext}
      onClick={scrollNext}
      className={cn(
        'absolute z-10 flex size-8 items-center justify-center rounded-full border border-border-glass bg-background/80 shadow-sm backdrop-blur-sm transition-all',
        'hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50',
        orientation === 'horizontal'
          ? '-right-12 top-1/2 -translate-y-1/2'
          : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
        className
      )}
      {...props}
    >
      <ArrowRightIcon className="size-4" />
      <span className="sr-only">Next slide</span>
    </button>
  )
}

export { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, useCarousel }
