import { useEffect, useId, useRef } from "react";
import { createPortal } from "react-dom";
import type { ReactNode } from "react";
import {
  hasOpenDialog,
  isTopDialog,
  popDialog,
  pushDialog,
} from "./dialogStack";

interface ModalProps {
  open: boolean;
  title: ReactNode;
  /** Accessible name for the dialog when `title` is not plain text. */
  ariaLabel?: string;
  onClose: () => void;
  /** Footer slot, typically the action buttons. */
  footer?: ReactNode;
  /** Wider layout for content-heavy dialogs (key management). */
  wide?: boolean;
  /** Disable overlay-click and Escape dismissal (e.g. during a pending action). */
  dismissable?: boolean;
  children: ReactNode;
}

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

/**
 * Accessible modal dialog. Closes on Escape and overlay click (unless
 * `dismissable` is false), traps Tab focus inside, marks the rest of the page
 * inert, and restores focus to the trigger on close.
 */
export function Modal({
  open,
  title,
  ariaLabel,
  onClose,
  footer,
  wide,
  dismissable = true,
  children,
}: ModalProps) {
  const panelRef = useRef<HTMLDivElement>(null);
  const lastFocused = useRef<HTMLElement | null>(null);
  const titleId = useId();

  useEffect(() => {
    if (!open) return;
    const panel = panelRef.current;
    lastFocused.current = document.activeElement as HTMLElement | null;
    const token = pushDialog();

    // Mark everything outside the dialog inert so focus and clicks can't reach
    // the page behind. Dialogs are portaled to <body>, so this targets #root.
    const root = document.getElementById("root");
    root?.setAttribute("inert", "");

    // Move focus to the first focusable control, falling back to the panel.
    const first = panel?.querySelector<HTMLElement>(FOCUSABLE);
    (first ?? panel)?.focus();

    function onKey(e: KeyboardEvent) {
      // Only the topmost dialog reacts (don't close a stack all at once).
      if (!isTopDialog(token)) return;
      if (e.key === "Escape" && dismissable) {
        e.stopPropagation();
        onClose();
        return;
      }
      if (e.key !== "Tab" || !panel) return;
      // Cycle focus within the dialog.
      const items = Array.from(
        panel.querySelectorAll<HTMLElement>(FOCUSABLE),
      ).filter((el) => el.offsetParent !== null || el === document.activeElement);
      if (items.length === 0) {
        e.preventDefault();
        panel.focus();
        return;
      }
      const firstEl = items[0];
      const lastEl = items[items.length - 1];
      const active = document.activeElement as HTMLElement;
      if (e.shiftKey && (active === firstEl || active === panel)) {
        e.preventDefault();
        lastEl.focus();
      } else if (!e.shiftKey && active === lastEl) {
        e.preventDefault();
        firstEl.focus();
      }
    }
    document.addEventListener("keydown", onKey, true);

    // Lock background scroll while the dialog is open.
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";

    return () => {
      document.removeEventListener("keydown", onKey, true);
      document.body.style.overflow = prevOverflow;
      popDialog(token);
      // Only lift inert once the last dialog has closed.
      if (!hasOpenDialog()) root?.removeAttribute("inert");
      lastFocused.current?.focus?.();
    };
  }, [open, dismissable, onClose]);

  if (!open) return null;

  const labelledBy = ariaLabel ? undefined : titleId;

  return createPortal(
    <div
      className="modal__overlay"
      onMouseDown={(e) => {
        if (e.target === e.currentTarget && dismissable) onClose();
      }}
    >
      <div
        ref={panelRef}
        className={`modal${wide ? " modal--wide" : ""}`}
        role="dialog"
        aria-modal="true"
        aria-label={ariaLabel}
        aria-labelledby={labelledBy}
        tabIndex={-1}
      >
        <header className="modal__head">
          <h2 className="modal__title" id={titleId}>
            {title}
          </h2>
          {dismissable && (
            <button
              type="button"
              className="iconbtn"
              onClick={onClose}
              aria-label="Close dialog"
            >
              <svg
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                aria-hidden="true"
              >
                <path d="M6 6l12 12M18 6 6 18" />
              </svg>
            </button>
          )}
        </header>
        <div className="modal__body">{children}</div>
        {footer && <footer className="modal__footer">{footer}</footer>}
      </div>
    </div>,
    document.body,
  );
}
