// frontend\src\components\ui\HoverPopover.tsx

'use client'

import {
  type PropsWithChildren,
  type ReactNode,
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
} from 'react'
import { createPortal } from 'react-dom'
import Card from './Card'

type Pos = { left: number; top: number }

type HoverPopoverAPI = { close: () => void }

type HoverPopoverProps = PropsWithChildren<{
  content: ReactNode | ((open: boolean, api: HoverPopoverAPI) => ReactNode)
  onOpenChange?: (open: boolean) => void
}>

export default function HoverPopover({ children, content, onOpenChange }: HoverPopoverProps) {
  const triggerRef = useRef<HTMLDivElement>(null)
  const popoverRef = useRef<HTMLDivElement>(null)

  const [open, setOpen] = useState(false)
  const [pos, setPos] = useState<Pos | null>(null)

  const setOpenState = (next: boolean) => {
    setOpen((prev) => (prev === next ? prev : next))
  }

  useEffect(() => {
    onOpenChange?.(open)
  }, [open, onOpenChange])

  const closeTimeoutRef = useRef<number | null>(null)

  const clearCloseTimeout = () => {
    if (closeTimeoutRef.current !== null) {
      window.clearTimeout(closeTimeoutRef.current)
      closeTimeoutRef.current = null
    }
  }

  const scheduleClose = () => {
    clearCloseTimeout()
    closeTimeoutRef.current = window.setTimeout(() => {
      setOpenState(false)
      closeTimeoutRef.current = null
    }, 150)
  }

  const handleEnter = () => {
    clearCloseTimeout()
    setOpenState(true)
  }

  const handleLeave = () => {
    scheduleClose()
  }

  const close = () => {
    clearCloseTimeout()
    setOpenState(false)
  }

  const computePos = () => {
    const trigger = triggerRef.current
    const pop = popoverRef.current
    if (!trigger || !pop) return

    const gap = 8
    const pad = 8
    const r = trigger.getBoundingClientRect()
    const pr = pop.getBoundingClientRect()

    let top = r.bottom + gap
    const bottomOverflow = top + pr.height > window.innerHeight - pad

    if (bottomOverflow) {
      const above = r.top - pr.height - gap
      if (above >= pad) top = above
      else top = Math.max(pad, window.innerHeight - pr.height - pad)
    }

    let left = r.left
    if (left + pr.width > window.innerWidth - pad) {
      left = window.innerWidth - pr.width - pad
    }
    left = Math.max(pad, left)

    setPos({ left, top })
  }

  useLayoutEffect(() => {
    if (!open) return
    const id = requestAnimationFrame(() => computePos())
    return () => cancelAnimationFrame(id)
  }, [open])

  useEffect(() => {
    if (!open) return
    const onMove = () => requestAnimationFrame(() => computePos())
    window.addEventListener('resize', onMove)
    window.addEventListener('scroll', onMove, true)
    return () => {
      window.removeEventListener('resize', onMove)
      window.removeEventListener('scroll', onMove, true)
    }
  }, [open])

  useEffect(() => {
    return () => clearCloseTimeout()
  }, [])

  const renderContent = () =>
    typeof content === 'function'
      ? (content as any)(open, { close })
      : content

  return (
    <>
      <div
        ref={triggerRef}
        className="inline-flex"
        onMouseEnter={handleEnter}
        onMouseLeave={handleLeave}
      >
        {children}
      </div>

      {open &&
        createPortal(
          <div
            ref={popoverRef}
            className="fixed z-[120]"
            style={{
              left: pos?.left ?? -9999,
              top: pos?.top ?? -9999,
              visibility: pos ? 'visible' : 'hidden',
            }}
            onMouseEnter={handleEnter}
            onMouseLeave={handleLeave}
          >
            <Card
              className="shadow-lg ring-1 ring-black/10 dark:ring-white/10 max-w-[calc(100vw-16px)]"
              noBodyPadding
            >
              {renderContent()}
            </Card>
          </div>,
          document.body
        )}
    </>
  )
}