import * as React from 'react'

import { cn } from './utils'

/* ------------------------------------------------------------------ */
/*  HoverCard                                                          */
/* ------------------------------------------------------------------ */

interface HoverCardContextValue {
  open: boolean
  openDelay: number
  closeDelay: number
  onOpenChange: (open: boolean) => void
}

const HoverCardContext = React.createContext<HoverCardContextValue>({
  open: false,
  openDelay: 300,
  closeDelay: 200,
  onOpenChange: () => {},
})

export interface HoverCardProps {
  children: React.ReactNode
  /** Delay in ms before the card opens (default: 300) */
  openDelay?: number
  /** Delay in ms before the card closes (default: 200) */
  closeDelay?: number
  /** Controlled open state */
  open?: boolean
  /** Callback when open state changes */
  onOpenChange?: (open: boolean) => void
}

function HoverCard({
  children,
  openDelay = 300,
  closeDelay = 200,
  open: controlledOpen,
  onOpenChange: controlledOnOpenChange,
}: HoverCardProps) {
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)

  const isControlled = controlledOpen !== undefined
  const open = isControlled ? controlledOpen : uncontrolledOpen
  const onOpenChange = isControlled ? (controlledOnOpenChange ?? (() => {})) : setUncontrolledOpen

  const value = React.useMemo(
    () => ({ open, openDelay, closeDelay, onOpenChange }),
    [open, openDelay, closeDelay, onOpenChange]
  )

  return <HoverCardContext.Provider value={value}>{children}</HoverCardContext.Provider>
}

/* ------------------------------------------------------------------ */
/*  HoverCardTrigger                                                   */
/* ------------------------------------------------------------------ */

function HoverCardTrigger({ className, children, ...props }: React.ComponentProps<'div'>) {
  const { openDelay, closeDelay, onOpenChange } = React.useContext(HoverCardContext)
  const showTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)
  const hideTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)

  const handleMouseEnter = React.useCallback(() => {
    if (hideTimeoutRef.current !== null) {
      clearTimeout(hideTimeoutRef.current)
      hideTimeoutRef.current = null
    }
    showTimeoutRef.current = setTimeout(() => {
      onOpenChange(true)
    }, openDelay)
  }, [openDelay, onOpenChange])

  const handleMouseLeave = React.useCallback(() => {
    if (showTimeoutRef.current !== null) {
      clearTimeout(showTimeoutRef.current)
      showTimeoutRef.current = null
    }
    hideTimeoutRef.current = setTimeout(() => {
      onOpenChange(false)
    }, closeDelay)
  }, [closeDelay, onOpenChange])

  React.useEffect(() => {
    return () => {
      if (showTimeoutRef.current !== null) clearTimeout(showTimeoutRef.current)
      if (hideTimeoutRef.current !== null) clearTimeout(hideTimeoutRef.current)
    }
  }, [])

  return (
    <div
      data-slot="hover-card-trigger"
      aria-haspopup="dialog"
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      className={cn('inline-block', className)}
      {...props}
    >
      {children}
    </div>
  )
}

/* ------------------------------------------------------------------ */
/*  HoverCardContent                                                   */
/* ------------------------------------------------------------------ */

export type HoverCardContentProps = React.ComponentProps<'div'> & {
  /** Side relative to the trigger */
  side?: 'top' | 'bottom'
  /** Alignment along the trigger */
  align?: 'start' | 'center' | 'end'
}

function HoverCardContent({
  className,
  side = 'bottom',
  align = 'center',
  children,
  ...props
}: HoverCardContentProps) {
  const { open, closeDelay, onOpenChange } = React.useContext(HoverCardContext)
  const hideTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)

  const handleMouseEnter = React.useCallback(() => {
    if (hideTimeoutRef.current !== null) {
      clearTimeout(hideTimeoutRef.current)
      hideTimeoutRef.current = null
    }
    onOpenChange(true)
  }, [onOpenChange])

  const handleMouseLeave = React.useCallback(() => {
    hideTimeoutRef.current = setTimeout(() => {
      onOpenChange(false)
    }, closeDelay)
  }, [closeDelay, onOpenChange])

  React.useEffect(() => {
    return () => {
      if (hideTimeoutRef.current !== null) clearTimeout(hideTimeoutRef.current)
    }
  }, [])

  if (!open) {
    return null
  }

  return (
    <div
      data-slot="hover-card-content"
      role="dialog"
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      className={cn(
        'absolute z-50 w-64 rounded-xl border border-border-glass bg-glass p-4 shadow-xl backdrop-blur-xl',
        'animate-in fade-in-0 zoom-in-95',
        side === 'top' && 'bottom-full mb-2',
        side === 'bottom' && 'top-full mt-2',
        align === 'start' && 'left-0',
        align === 'center' && 'left-1/2 -translate-x-1/2',
        align === 'end' && 'right-0',
        className
      )}
      {...props}
    >
      {children}
    </div>
  )
}

export { HoverCard, HoverCardTrigger, HoverCardContent }
