import { useEffect, useLayoutEffect, useReducer } from 'react';

/**
 * Resolved HTML `dir` keyword: effective writing direction after `dir`, then CSS
 * `direction`, then document root.
 */

/**
 * Resolves the effective `dir` for an element (`rtl` or `ltr`), including JSDOM where
 * `getComputedStyle(el).direction` is often empty while `dir` is set on the root.
 *
 * @param el - DOM node whose effective direction is resolved.
 * @returns `rtl` or `ltr`.
 */
export function elementDir(el) {
  const ownDir = el.getAttribute('dir');
  if (ownDir === 'rtl') return 'rtl';
  if (ownDir === 'ltr') return 'ltr';
  const {
    direction
  } = getComputedStyle(el);
  if (direction === 'rtl' || direction === 'ltr') {
    return direction;
  }
  return document.documentElement.getAttribute('dir') === 'rtl' ? 'rtl' : 'ltr';
}

/**
 * Ref whose `current` may be any DOM {@link Element} subclass (`HTMLElement`, `SVGElement`,
 * `HTMLButtonElement`, etc.). For structural/minimal types (e.g. tests), intersect with
 * `Element` so `current` is still typed as an `Element` (e.g. `Pick<HTMLAnchorElement, 'id'> & Element`).
 *
 * @template T - DOM element type for `current`; defaults to {@link Element}.
 */

function resolveElement(elementRef) {
  return elementRef?.current instanceof Element ? elementRef.current : document.documentElement;
}

/**
 * Returns the effective `dir` for the resolved element (`rtl` or `ltr`), and updates when `dir`
 * changes on the document subtree or after layout (so `ref.current` is current).
 * Resolution uses {@link elementDir}.
 *
 * @template T - DOM element type for the optional ref; defaults to {@link Element}.
 * @param elementRef - Optional ref; when missing or `current` is not an `Element`, uses `document.documentElement`.
 * @returns Effective direction for the resolved element, or `ltr` when `document` is undefined (SSR).
 */
export function useElementDir(elementRef) {
  const [, bump] = useReducer(n => n + 1, 0);
  useLayoutEffect(() => {
    bump();
  }, [elementRef]);
  useEffect(() => {
    const observer = new MutationObserver(() => {
      bump();
    });
    observer.observe(document.documentElement, {
      attributeFilter: ['dir'],
      attributes: true,
      subtree: true
    });
    return () => observer.disconnect();
  }, []);
  if (typeof document === 'undefined') {
    return 'ltr';
  }
  return elementDir(resolveElement(elementRef));
}