'use client'

import * as React from 'react'
import type { CarouselApi } from '@admin/components/ui/carousel'
import type { AdminMedia } from '@admin/types'
import type { DragEndEvent, DragStartEvent } from '@dnd-kit/core'
import { MediaGalleryDialog } from '@admin/components/shared/media/media-gallery-dialog'
import { Card, CardContent, CardFooter } from '@admin/components/ui/card'
import { Carousel, CarouselContent, CarouselItem } from '@admin/components/ui/carousel'
import { Skeleton } from '@admin/components/ui/skeleton'
import { useMediaByIds } from '@admin/hooks/use-media'
import {
  closestCenter,
  DndContext,
  DragOverlay,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors
} from '@dnd-kit/core'
import { arrayMove, horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable'
import { Plus } from 'lucide-react'
import { MediaField } from './media-field'
import { MediaFieldEmptyState } from './media-field-empty-state'
import { SortableGalleryItem } from './sortable-gallery-item'

interface GalleryFieldProps {
  value?: string[]
  onChange: (value: string[]) => void
  disabled?: boolean
  className?: string
  maxItems?: number
}

export function GalleryField({
  value = [],
  onChange,
  disabled,
  className,
  maxItems
}: GalleryFieldProps) {
  const [open, setOpen] = React.useState(false)
  const [activeId, setActiveId] = React.useState<string | null>(null)
  const { data: mediaItems } = useMediaByIds(value)
  const carouselRootRef = React.useRef<HTMLDivElement | null>(null)
  const carouselApiRef = React.useRef<CarouselApi>(undefined)

  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
    useSensor(KeyboardSensor)
  )

  function handleDragStart(event: DragStartEvent) {
    setActiveId(String(event.active.id))
  }

  function handleDragEnd(event: DragEndEvent) {
    setActiveId(null)
    const { active, over } = event
    if (!over || active.id === over.id) return

    const oldIndex = value.indexOf(active.id as string)
    const newIndex = value.indexOf(over.id as string)
    if (oldIndex !== -1 && newIndex !== -1) {
      onChange(arrayMove(value, oldIndex, newIndex))
    }
  }

  function handleDragCancel() {
    setActiveId(null)
  }

  function handleSelect(selected: AdminMedia | AdminMedia[]) {
    const items = Array.isArray(selected) ? selected : [selected]
    const newIds = items.map((m) => m.id).filter((id) => !value.includes(id))
    const merged = [...value, ...newIds]
    onChange(maxItems ? merged.slice(0, maxItems) : merged)
  }

  function handleRemove(id: string) {
    onChange(value.filter((v) => v !== id))
  }

  function handleReplace(currentId: string, nextId: string) {
    if (!nextId) {
      handleRemove(currentId)
      return
    }

    if (nextId === currentId) return

    onChange(
      value.flatMap((id) => {
        if (id !== currentId) return [id]
        return value.includes(nextId) ? [] : [nextId]
      })
    )
  }

  function handleCarouselApi(api: CarouselApi) {
    carouselApiRef.current = api
  }

  const hasSelectedMedia = value.length > 0

  React.useEffect(() => {
    if (!hasSelectedMedia) return

    const carouselRoot = carouselRootRef.current
    if (!carouselRoot) return

    function handleWheel(event: WheelEvent) {
      const api = carouselApiRef.current
      if (!api) return

      const isHorizontalIntent = Math.abs(event.deltaX) >= Math.abs(event.deltaY)
      const delta = isHorizontalIntent ? event.deltaX : event.deltaY
      if (Math.abs(delta) < 1) return

      const engine = api.internalEngine()
      const target = engine.limit.constrain(engine.target.get() - delta)
      const distance = target - engine.target.get()

      if (Math.abs(distance) < 0.5) {
        if (isHorizontalIntent) {
          event.preventDefault()
        }
        return
      }

      event.preventDefault()
      const scrollBody = engine.scrollBody
      const setScrollFriction = scrollBody.useFriction
      const setScrollDuration = scrollBody.useDuration
      setScrollFriction(0)
      setScrollDuration(0)
      engine.scrollTo.distance(distance, false)
    }

    carouselRoot.addEventListener('wheel', handleWheel, { passive: false })
    return () => {
      carouselRoot.removeEventListener('wheel', handleWheel)
    }
  }, [hasSelectedMedia])

  const mediaMap = React.useMemo(() => {
    const map = new Map<string, AdminMedia>()
    if (mediaItems) {
      for (let i = 0; i < value.length; i++) {
        const m = mediaItems[i]
        if (m) map.set(m.id, m)
      }
    }
    return map
  }, [mediaItems, value])

  const canAdd = !maxItems || value.length < maxItems
  const activeItem = activeId ? mediaMap.get(activeId) : undefined

  return (
    <div className={className}>
      {hasSelectedMedia ? (
        <DndContext
          sensors={sensors}
          collisionDetection={closestCenter}
          onDragStart={handleDragStart}
          onDragEnd={handleDragEnd}
          onDragCancel={handleDragCancel}
        >
          <div ref={carouselRootRef} className="w-full overscroll-x-contain">
            <Carousel
              opts={{
                align: 'start',
                containScroll: 'trimSnaps',
                duration: 0,
                dragFree: true,
                watchDrag: (_, event) => {
                  const target = event.target
                  return !(
                    target instanceof Element &&
                    target.closest(
                      '[data-gallery-drag-handle], [data-media-field-action], button, a'
                    )
                  )
                }
              }}
              setApi={handleCarouselApi}
              className="w-full"
            >
              <CarouselContent className="-ml-3">
                {canAdd && !disabled ? (
                  <CarouselItem className="basis-[184px] pl-3">
                    <Card className="h-full" onClick={() => setOpen(true)}>
                      <CardContent className="flex cursor-pointer flex-col p-2">
                        <div className="flex aspect-square size-full flex-col items-center justify-center gap-1 overflow-hidden rounded-sm border border-border bg-background text-muted-foreground transition-colors hover:text-foreground">
                          <Plus className="size-6" strokeWidth={1.5} />
                          <span className="text-sm">Add Images</span>
                        </div>
                      </CardContent>
                      <CardFooter>
                        <Skeleton className="h-4 w-3/4" />
                      </CardFooter>
                    </Card>
                  </CarouselItem>
                ) : null}

                <SortableContext items={value} strategy={horizontalListSortingStrategy}>
                  {value.map((id) => {
                    const item = mediaMap.get(id)
                    return (
                      <CarouselItem key={id} className="basis-[184px] pl-3">
                        <SortableGalleryItem id={id} disabled={disabled}>
                          {item ? (
                            <MediaField
                              value={id}
                              media={item}
                              onChange={(nextId) => handleReplace(id, nextId)}
                              onRemove={() => handleRemove(id)}
                              disabled={disabled}
                              isRemovable
                              className="**:data-media-field-preview:aspect-square! [&_[data-media-field-preview]_img]:object-cover!"
                              footerMode="filename"
                            />
                          ) : (
                            <Card>
                              <CardContent className="flex flex-col p-2">
                                <div className="flex aspect-square size-full overflow-hidden rounded-sm border border-border bg-background">
                                  <Skeleton className="size-full" />
                                </div>
                              </CardContent>
                              <CardFooter>
                                <Skeleton className="h-4 w-3/4" />
                              </CardFooter>
                            </Card>
                          )}
                        </SortableGalleryItem>
                      </CarouselItem>
                    )
                  })}
                </SortableContext>
              </CarouselContent>
            </Carousel>
          </div>
          <DragOverlay>
            {activeId ? (
              <div className="w-[184px] scale-[0.98] cursor-grabbing opacity-95">
                {activeItem ? (
                  <MediaField
                    value={activeId}
                    media={activeItem}
                    onChange={(nextId) => handleReplace(activeId, nextId)}
                    onRemove={() => handleRemove(activeId)}
                    disabled
                    isRemovable
                    className="**:data-media-field-preview:aspect-square! [&_[data-media-field-preview]_img]:object-cover!"
                    footerMode="filename"
                  />
                ) : (
                  <Card>
                    <CardContent className="flex flex-col p-2">
                      <div className="flex aspect-square size-full overflow-hidden rounded-sm border border-border bg-background">
                        <Skeleton className="size-full" />
                      </div>
                    </CardContent>
                    <CardFooter>
                      <Skeleton className="h-4 w-3/4" />
                    </CardFooter>
                  </Card>
                )}
              </div>
            ) : null}
          </DragOverlay>
        </DndContext>
      ) : null}

      {!hasSelectedMedia ? (
        <MediaFieldEmptyState
          addButtonLabel="Add Images"
          disabled={disabled}
          mode="multi"
          open={open}
          onOpenChange={setOpen}
          onSelect={handleSelect}
          showAddButton
        />
      ) : null}

      {hasSelectedMedia ? (
        <MediaGalleryDialog
          open={open}
          onOpenChange={setOpen}
          onSelect={handleSelect}
          mode="multi"
        />
      ) : null}
    </div>
  )
}
