'use client'

import * as React from 'react'
import type { IconData } from './icons-data'
import type { VirtualItem } from '@tanstack/react-virtual'
import type { IconName } from 'lucide-react/dynamic'
import { Button } from '@admin/components/ui/button'
import { Input } from '@admin/components/ui/input'
import { Popover, PopoverContent, PopoverTrigger } from '@admin/components/ui/popover'
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger
} from '@admin/components/ui/tooltip'
import { cn } from '@admin/utils/shared/cn'
import { useVirtualizer } from '@tanstack/react-virtual'
import Fuse from 'fuse.js'
import { DynamicIcon, dynamicIconImports } from 'lucide-react/dynamic'
import { useCallback, useMemo, useState } from 'react'
import { useDebounceValue } from 'usehooks-ts'
import { MemoizedIcon } from './icon-picker/memoized-icon'
import { IconsColumnSkeleton } from './icons-column-skeleton'
import { iconsData } from './icons-data'

const DEFAULT_ICONS = iconsData.filter((icon: IconData) => {
  return icon.name in dynamicIconImports
})

interface IconPickerProps extends Omit<
  React.ComponentPropsWithoutRef<typeof PopoverTrigger>,
  'onSelect' | 'onOpenChange'
> {
  value?: string
  defaultValue?: string
  onValueChange?: (value: string) => void
  open?: boolean
  defaultOpen?: boolean
  onOpenChange?: (open: boolean) => void
  searchable?: boolean
  searchPlaceholder?: string
  triggerPlaceholder?: string
  iconsList?: IconData[]
  categorized?: boolean
  modal?: boolean
}

const IconPicker = React.forwardRef<React.ComponentRef<typeof PopoverTrigger>, IconPickerProps>(
  (
    {
      value,
      defaultValue,
      onValueChange,
      open,
      defaultOpen,
      onOpenChange,
      children,
      searchable = true,
      searchPlaceholder = 'Search for an icon...',
      triggerPlaceholder = 'Select an Icon',
      iconsList,
      categorized = true,
      modal = false,
      ...props
    },
    ref
  ) => {
    const [selectedIcon, setSelectedIcon] = useState<string | undefined>(defaultValue)
    const [isOpen, setIsOpen] = useState(defaultOpen || false)
    const [search, setSearch] = useDebounceValue('', 100)
    const [isPopoverVisible, setIsPopoverVisible] = useState(false)

    const iconsToUse = useMemo(() => iconsList || DEFAULT_ICONS, [iconsList])
    const showIconSkeleton = iconsToUse.length === 0

    const fuseInstance = useMemo(() => {
      return new Fuse(iconsToUse, {
        keys: ['name', 'tags', 'categories'],
        threshold: 0.3,
        ignoreLocation: true,
        includeScore: true
      })
    }, [iconsToUse])

    const filteredIcons = useMemo(() => {
      if (search.trim() === '') {
        return iconsToUse
      }

      const results = fuseInstance.search(search.toLowerCase().trim())
      return results.map((result) => result.item)
    }, [search, iconsToUse, fuseInstance])

    const categorizedIcons = useMemo(() => {
      if (!categorized || search.trim() !== '') {
        return [{ name: 'All Icons', icons: filteredIcons }]
      }

      const categories = new Map<string, IconData[]>()

      filteredIcons.forEach((icon) => {
        if (icon.categories && icon.categories.length > 0) {
          icon.categories.forEach((category) => {
            if (!categories.has(category)) {
              categories.set(category, [])
            }
            categories.get(category)?.push(icon)
          })
        } else {
          const category = 'Other'
          if (!categories.has(category)) {
            categories.set(category, [])
          }
          categories.get(category)?.push(icon)
        }
      })

      return Array.from(categories.entries())
        .map(([name, icons]) => ({ name, icons }))
        .sort((a, b) => a.name.localeCompare(b.name))
    }, [filteredIcons, categorized, search])

    const virtualItems = useMemo(() => {
      const items: Array<{
        type: 'category' | 'row'
        categoryIndex: number
        rowIndex?: number
        icons?: IconData[]
      }> = []

      categorizedIcons.forEach((category, categoryIndex) => {
        items.push({ type: 'category', categoryIndex })

        const rows = []
        for (let i = 0; i < category.icons.length; i += 5) {
          rows.push(category.icons.slice(i, i + 5))
        }

        rows.forEach((rowIcons, rowIndex) => {
          items.push({
            type: 'row',
            categoryIndex,
            rowIndex,
            icons: rowIcons
          })
        })
      })

      return items
    }, [categorizedIcons])

    const categoryIndices = useMemo(() => {
      const indices: Record<string, number> = {}

      virtualItems.forEach((item, index) => {
        if (item.type === 'category') {
          indices[categorizedIcons[item.categoryIndex].name] = index
        }
      })

      return indices
    }, [virtualItems, categorizedIcons])

    const parentRef = React.useRef<HTMLDivElement>(null)

    const virtualizer = useVirtualizer({
      count: virtualItems.length,
      getScrollElement: () => parentRef.current,
      estimateSize: (index) => (virtualItems[index].type === 'category' ? 25 : 40),
      paddingEnd: 2,
      gap: 10,
      overscan: 5
    })

    const handleValueChange = useCallback(
      (icon: IconName) => {
        if (value === undefined) {
          setSelectedIcon(icon)
        }
        onValueChange?.(icon)
      },
      [value, onValueChange]
    )

    const handleOpenChange = useCallback(
      (newOpen: boolean) => {
        setSearch('')
        if (open === undefined) {
          setIsOpen(newOpen)
        }
        onOpenChange?.(newOpen)

        setIsPopoverVisible(newOpen)

        if (newOpen) {
          setTimeout(() => {
            virtualizer.measure()
          }, 1)
        }
      },
      [open, onOpenChange, virtualizer, setSearch]
    )

    const handleIconClick = useCallback(
      (iconName: IconName) => {
        handleValueChange(iconName)
        setIsOpen(false)
        setSearch('')
      },
      [handleValueChange, setSearch]
    )

    const handleSearchChange = useCallback(
      (e: React.ChangeEvent<HTMLInputElement>) => {
        setSearch(e.target.value)

        if (parentRef.current) {
          parentRef.current.scrollTop = 0
        }

        virtualizer.scrollToOffset(0)
      },
      [virtualizer, setSearch]
    )

    const scrollToCategory = useCallback(
      (categoryName: string) => {
        const categoryIndex = categoryIndices[categoryName]

        if (categoryIndex !== undefined && virtualizer) {
          virtualizer.scrollToIndex(categoryIndex, {
            align: 'start',
            behavior: 'smooth'
          })
        }
      },
      [categoryIndices, virtualizer]
    )

    const categoryButtons = useMemo(() => {
      if (!categorized || search.trim() !== '') return null

      return categorizedIcons.map((category) => (
        <Button
          key={category.name}
          variant={'outline'}
          className="text-xs"
          onClick={(e) => {
            e.stopPropagation()
            scrollToCategory(category.name)
          }}
        >
          {category.name.charAt(0).toUpperCase() + category.name.slice(1)}
        </Button>
      ))
    }, [categorizedIcons, scrollToCategory, categorized, search])

    const renderIcon = useCallback(
      (icon: IconData) => (
        <TooltipProvider key={icon.name}>
          <Tooltip>
            <TooltipTrigger
              className={cn(
                'rounded-md border p-2 transition hover:bg-foreground/10',
                'flex items-center justify-center'
              )}
              onClick={() => handleIconClick(icon.name as IconName)}
            >
              <MemoizedIcon name={icon.name as IconName} />
            </TooltipTrigger>
            <TooltipContent>
              <p>{icon.name}</p>
            </TooltipContent>
          </Tooltip>
        </TooltipProvider>
      ),
      [handleIconClick]
    )

    const renderVirtualContent = useCallback(() => {
      if (filteredIcons.length === 0) {
        return <div className="text-center text-muted-foreground">No icon found</div>
      }

      return (
        <div
          className="relative w-full overscroll-contain"
          style={{
            height: `${virtualizer.getTotalSize()}px`
          }}
        >
          {virtualizer.getVirtualItems().map((virtualItem: VirtualItem) => {
            const item = virtualItems[virtualItem.index]

            if (!item) return null

            const itemStyle = {
              position: 'absolute' as const,
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualItem.size}px`,
              transform: `translateY(${virtualItem.start}px)`
            }

            if (item.type === 'category') {
              return (
                <div key={virtualItem.key} style={itemStyle} className="z-10 bg-background">
                  <h3 className="text-sm font-medium capitalize">
                    {categorizedIcons[item.categoryIndex].name}
                  </h3>
                  <div className="h-[1px] w-full bg-foreground/10" />
                </div>
              )
            }

            return (
              <div key={virtualItem.key} data-index={virtualItem.index} style={itemStyle}>
                <div className="grid w-full grid-cols-5 gap-2">{item.icons?.map(renderIcon)}</div>
              </div>
            )
          })}
        </div>
      )
    }, [virtualizer, virtualItems, categorizedIcons, filteredIcons, renderIcon])

    React.useEffect(() => {
      if (isPopoverVisible) {
        const timer = setTimeout(() => {
          virtualizer.measure()
        }, 10)

        const resizeObserver = new ResizeObserver(() => {
          virtualizer.measure()
        })

        if (parentRef.current) {
          resizeObserver.observe(parentRef.current)
        }

        return () => {
          clearTimeout(timer)
          resizeObserver.disconnect()
        }
      }
    }, [isPopoverVisible, virtualizer])

    const selectedIconName = (value || selectedIcon) as IconName | undefined
    const trigger = React.isValidElement(children) ? (
      children
    ) : (
      <Button variant="outline" size="lg">
        {selectedIconName ? <DynamicIcon name={selectedIconName} /> : triggerPlaceholder}
      </Button>
    )

    return (
      <Popover open={open ?? isOpen} onOpenChange={handleOpenChange} modal={modal}>
        <PopoverTrigger ref={ref} render={trigger} {...props} />
        <PopoverContent className="w-64 p-2">
          {searchable && (
            <Input placeholder={searchPlaceholder} onChange={handleSearchChange} className="mb-2" />
          )}
          {categorized && search.trim() === '' && (
            <div className="mt-2 flex flex-row gap-1 overflow-x-auto pb-2">{categoryButtons}</div>
          )}
          <div
            ref={parentRef}
            className="max-h-60 overflow-auto"
            style={{ scrollbarWidth: 'thin' }}
          >
            {showIconSkeleton ? <IconsColumnSkeleton /> : renderVirtualContent()}
          </div>
        </PopoverContent>
      </Popover>
    )
  }
)
IconPicker.displayName = 'IconPicker'

export { IconPicker, type IconData, type IconName }
