import type { MutableRefObject, SyntheticEvent } from "react";
import { useCallback, useEffect, useState } from "react";
import { supportsDialog } from "../../utils";
import { useBodyScrollLock } from "@simplybusiness/mobius-hooks";
import { useDialogPolyfill } from "../useDialogPolyfill";

export type TransitionProps = {
  isEnabled: boolean;
  CSSVariable: string;
};

export type useDialogProps = {
  ref: MutableRefObject<HTMLDialogElement | null>;
  transition: TransitionProps;
  isOpen: boolean;
  onOpen?: () => void;
  onClose?: () => void;
};

const TRANSITION_CLASS_NAME = "--transition";
const FALLBACK_TRANSITION_DURATION = 0;

export const useDialog = (props: useDialogProps) => {
  const { ref, isOpen, transition, onOpen, onClose } = props;
  const [hasDialogSupport, setHasDialogSupport] = useState(false);
  const shouldTransition = hasDialogSupport && transition.isEnabled;
  const { polyfillDialog } = useDialogPolyfill();

  useEffect(() => {
    setHasDialogSupport(supportsDialog());
  }, []);

  // Read CSS variable value as number
  const TRANSITION_DURATION_IN_MS =
    (ref.current &&
      Number(
        getComputedStyle(ref.current)
          .getPropertyValue(transition.CSSVariable)
          .replace("ms", ""),
      )) ||
    FALLBACK_TRANSITION_DURATION;

  useBodyScrollLock({ enabled: isOpen });

  const open = useCallback(() => {
    ref.current?.showModal();
    onOpen?.();
  }, [onOpen, ref]);

  const close = useCallback(() => {
    ref.current?.close();
    onClose?.();
  }, [onClose, ref]);

  // Add close handler, to enable closing transitions
  const handleClose = useCallback(
    (event?: SyntheticEvent<HTMLElement, Event>) => {
      if (event) {
        // Prevent default event coming from onCancel,
        // which is triggered by pressing ESC key
        event.preventDefault();
        // Ensure that nested `<dialog>` elements
        // don't close the parent
        event.stopPropagation();
      }

      if (shouldTransition) {
        ref.current?.classList.remove(TRANSITION_CLASS_NAME);
        // Delay close to allow exit transition
        setTimeout(() => close(), TRANSITION_DURATION_IN_MS);
      } else {
        close();
      }
    },
    [TRANSITION_DURATION_IN_MS, close, ref, shouldTransition],
  );

  useEffect(() => {
    async function toggleDialog() {
      if (isOpen && !ref.current?.open) {
        await polyfillDialog(ref);

        open();
        // Fix opening transition in Safari being skipped
        // by wrapping with setTimeout
        setTimeout(() => {
          ref.current?.classList.add(TRANSITION_CLASS_NAME);
        }, 0);
      } else if (!isOpen && ref.current?.open) {
        handleClose();
      }
    }

    void toggleDialog();
  }, [handleClose, isOpen, open, ref, polyfillDialog]);

  return {
    open,
    close: handleClose,
  };
};
