import React, {
  useState,
  useRef,
  useId,
  useCallback,
  useLayoutEffect,
  useEffect,
} from 'react'
import { createPortal } from 'react-dom'

function mergeRefs (...refs) {
  return (node) => {
    refs.forEach((ref) => {
      if (ref == null) return
      if (typeof ref === 'function') ref(node)
      else ref.current = node
    })
  }
}

function supportsAnchorPositioning () {
  if (typeof CSS === 'undefined' || typeof CSS.supports !== 'function') return false
  return CSS.supports('top', 'anchor(bottom)') || CSS.supports('position-anchor', 'auto')
}

export const Dropdown = ({
  trigger,
  children
}) => {
  const triggerRef = useRef(null)
  const [isOpen, setIsOpen] = useState(false)
  const [portalRoot, setPortalRoot] = useState(null)
  const anchorName = `--ossy-dropdown-${useId().replace(/:/g, '')}`
  const useAnchors = supportsAnchorPositioning()

  useEffect(() => {
    setPortalRoot(document.body)
  }, [])

  useLayoutEffect(() => {
    if (!useAnchors) return
    const el = triggerRef.current
    if (!el) return
    el.style.setProperty('anchor-name', anchorName)
    return () => el.style.removeProperty('anchor-name')
  }, [anchorName, useAnchors, isOpen])

  const setContentRef = useCallback((node) => {
    if (!node) return
    node.style.setProperty('position', 'fixed')
    node.style.setProperty('z-index', '101')
    node.style.setProperty('display', 'inline-block')

    const triggerEl = triggerRef.current
    if (!triggerEl) return

    if (useAnchors) {
      node.style.setProperty('position-anchor', anchorName)
      node.style.setProperty('top', 'anchor(bottom)')
      node.style.setProperty('left', 'anchor(left)')
      return
    }

    const rect = triggerEl.getBoundingClientRect()
    node.style.setProperty('top', `${rect.bottom}px`)
    node.style.setProperty('left', `${rect.left}px`)
  }, [anchorName, useAnchors])

  if (!React.isValidElement(trigger)) {
    throw new Error(
      'Dropdown `trigger` must be a single React element (e.g. `<Button />`).'
    )
  }

  if (trigger.type === React.Fragment) {
    throw new Error(
      'Dropdown `trigger` cannot be a Fragment; pass one element such as `<Button />`.'
    )
  }

  const triggerRefFromProps = trigger.props?.ref ?? trigger.ref
  const mergedRef = mergeRefs(triggerRef, triggerRefFromProps)

  const triggerNode = React.cloneElement(trigger, {
    ref: mergedRef,
    onClick: (event) => {
      trigger.props.onClick?.(event)
      if (event.defaultPrevented) return
      setIsOpen((open) => !open)
    },
  })

  return (
    <>
      {triggerNode}
      {isOpen && portalRoot && createPortal(
        <>
          <div
            style={{
              position: 'fixed',
              inset: 0,
              zIndex: 100,
              backgroundColor: 'hsla(0, 0%, 0%, .1)',
            }}
            onClick={() => setIsOpen(false)}
            aria-hidden
          />
          <div
            ref={setContentRef}
            onClick={() => setIsOpen(false)}
          >
            {children}
          </div>
        </>,
        portalRoot
      )}
    </>
  )
}
