const focusableSelector = [
  "a[href]",
  "button:not([disabled])",
  "input:not([disabled])",
  "select:not([disabled])",
  "textarea:not([disabled])",
  "[tabindex]:not([tabindex='-1'])",
].join(",");

export interface ModalControllerOptions {
  overlay: HTMLElement;
  dialog: HTMLElement;
  closeControls?: Iterable<HTMLElement>;
  closeOnBackdrop?: boolean;
  closeOnEscape?: boolean;
  lockBodyScroll?: boolean;
  onOpenChange?: (open: boolean) => void;
}

export interface ModalController {
  open: () => void;
  close: () => void;
  isOpen: () => boolean;
  destroy: () => void;
}

export function createModalController({
  overlay,
  dialog,
  closeControls = [],
  closeOnBackdrop = true,
  closeOnEscape = true,
  lockBodyScroll = true,
  onOpenChange,
}: ModalControllerOptions): ModalController {
  let openState = false;
  let previousFocus: HTMLElement | null = null;
  let previousOverflow = "";
  const controls = Array.from(closeControls);

  overlay.classList.add("nodedc-overlay", "nodedc-ui-root");
  dialog.classList.add("nodedc-window");
  dialog.setAttribute("role", "dialog");
  dialog.setAttribute("aria-modal", "true");
  dialog.tabIndex = -1;
  overlay.hidden = true;

  const focusFirst = () => {
    const first = dialog.querySelector<HTMLElement>(focusableSelector);
    (first ?? dialog).focus();
  };

  const open = () => {
    if (openState) return;
    openState = true;
    previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    previousOverflow = document.body.style.overflow;
    if (lockBodyScroll) document.body.style.overflow = "hidden";
    overlay.hidden = false;
    requestAnimationFrame(focusFirst);
    onOpenChange?.(true);
  };

  const close = () => {
    if (!openState) return;
    openState = false;
    overlay.hidden = true;
    if (lockBodyScroll) document.body.style.overflow = previousOverflow;
    previousFocus?.focus();
    onOpenChange?.(false);
  };

  const handleOverlayPointerDown = (event: PointerEvent) => {
    if (closeOnBackdrop && event.target === overlay) close();
  };
  const handleKeyDown = (event: KeyboardEvent) => {
    if (!openState) return;
    if (event.key === "Escape" && closeOnEscape) {
      event.preventDefault();
      close();
      return;
    }
    if (event.key !== "Tab") return;
    const focusable = Array.from(dialog.querySelectorAll<HTMLElement>(focusableSelector));
    if (focusable.length === 0) {
      event.preventDefault();
      dialog.focus();
      return;
    }
    const first = focusable[0];
    const last = focusable[focusable.length - 1];
    if (event.shiftKey && document.activeElement === first) {
      event.preventDefault();
      last.focus();
    } else if (!event.shiftKey && document.activeElement === last) {
      event.preventDefault();
      first.focus();
    }
  };

  overlay.addEventListener("pointerdown", handleOverlayPointerDown);
  document.addEventListener("keydown", handleKeyDown);
  controls.forEach((control) => control.addEventListener("click", close));

  return {
    open,
    close,
    isOpen: () => openState,
    destroy: () => {
      close();
      overlay.removeEventListener("pointerdown", handleOverlayPointerDown);
      document.removeEventListener("keydown", handleKeyDown);
      controls.forEach((control) => control.removeEventListener("click", close));
    },
  };
}

