import { useCallback, useEffect, useMemo, useRef } from "react";
import { warning } from "../../dev-warning";
import getBodyElement from "../get-body-element";
import visuallyHidden from "../visually-hidden-style";

export const getId = (contextId) => `rbd-announcement-${contextId}`;
export default function useAnnouncer(contextId) {
  const id = useMemo(() => getId(contextId), [contextId]);
  const ref = useRef(null);
  useEffect(
    function setup() {
      const el = document.createElement("div");
      // storing reference for usage in announce
      ref.current = el;

      // identifier
      el.id = id;

      // Aria live region

      // will force itself to be read
      el.setAttribute("aria-live", "assertive");
      // must read the whole thing every time
      el.setAttribute("aria-atomic", "true");

      // hide the element visually
      Object.assign(el.style, visuallyHidden);

      // Add to body
      getBodyElement().appendChild(el);
      return function cleanup() {
        // Not clearing the ref as it might be used by announce before the timeout expires

        // unmounting after a timeout to let any announcements
        // during a mount be published
        setTimeout(function remove() {
          // checking if element exists as the body might have been changed by things like 'turbolinks'
          const body = getBodyElement();
          if (body.contains(el)) {
            body.removeChild(el);
          }
          // if el was the current ref - clear it so that
          // we can get a warning if announce is called
          if (el === ref.current) {
            ref.current = null;
          }
        });
      };
    },
    [id]
  );
  const announce = useCallback((message) => {
    const el = ref.current;
    if (el) {
      el.textContent = message;
      return;
    }
    warning(`
      A screen reader message was trying to be announced but it was unable to do so.
      This can occur if you unmount your <DragDropContext /> in your onDragEnd.
      Consider calling provided.announce() before the unmount so that the instruction will
      not be lost for users relying on a screen reader.

      Message not passed to screen reader:

      "${message}"
    `);
  }, []);
  return announce;
}
