import React, {
  useState,
  useEffect,
  createContext,
  useContext,
  useRef,
} from "react"
import { createPortal } from "react-dom"
import useRerenderOnce from "../../hooks/use-rerender-once"

const canUseDOM = () => typeof window !== "undefined" && window.document

export const StackingContext = createContext()

export function PortalNode({ children, ...props }) {
  useRerenderOnce()
  const node = useRef()

  return (
    <StackingContext.Provider value={node.current}>
      <div {...props} ref={node}>
        {" "}
        {children}
      </div>
    </StackingContext.Provider>
  )
}

// Each Portal creates a new StackingContext, currently with no way to opt-out
// of this behavior. This means that anything rendered inside a nested portal
// will use the root of the previous portal.
function useRootNode() {
  return useContext(StackingContext) || document.body
}

function DOMPortal({ children }) {
  const rootNode = useRootNode()
  const [node] = useState(() => document.createElement("div"))

  useEffect(() => {
    rootNode.appendChild(node)

    return () => {
      node.remove()
    }
  }, [rootNode, node])

  return (
    <StackingContext.Provider value={node}>
      {createPortal(children, node)}
    </StackingContext.Provider>
  )
}

function ServerPortal() {
  return null
}

function Portal({ children }) {
  return canUseDOM() ? <DOMPortal>{children}</DOMPortal> : <ServerPortal />
}

export default Portal
