"use client";

import { useEffect, useRef } from "react";

/**
 * A dialog that behaves like one.
 *
 * Uses the native `<dialog>` element rather than a div with a high z-index, which gets
 * focus trapping, Escape, inertness of the page behind it and the top layer for free —
 * all things a hand-rolled overlay reliably gets wrong.
 */
/**
 * `wide` used to be a boolean. The card design panel needs a third width, and a boolean that
 * needs a third value is better replaced than extended (design D3).
 */
const SIZES = {
  default: "max-w-lg",
  wide: "max-w-3xl",
  full: "max-w-5xl",
} as const;

export default function Modal({
  title,
  onClose,
  children,
  footer,
  size = "default",
}: {
  title: string;
  onClose: () => void;
  children: React.ReactNode;
  /**
   * Rendered below the scrolling content, not inside it (design D3).
   *
   * A Save button placed at the end of the children is reachable only by scrolling past
   * everything above it — so in a tall modal the primary action on the screen is hidden by
   * default. Keeping it out of the scroll container pins it where it can always be reached.
   */
  footer?: React.ReactNode;
  size?: keyof typeof SIZES;
}) {
  const ref = useRef<HTMLDialogElement>(null);
  //: Whether the press that started this click landed on the backdrop. See `onClick`.
  const pressStartedOnBackdrop = useRef(false);

  useEffect(() => {
    const dialog = ref.current;
    if (!dialog) return;

    // A <dialog> without the `open` attribute is display:none. So if showModal() is missing
    // or throws, the modal mounts and stays invisible — indistinguishable from a button
    // that does nothing, and it takes a DOM inspection to discover otherwise. Falling back
    // to the plain `open` attribute loses the top layer and the focus trap, which is a much
    // better outcome than a form the operator cannot see.
    if (!dialog.open) {
      try {
        dialog.showModal();
      } catch {
        dialog.open = true;
      }
    }

    return () => dialog.close();
  }, []);

  return (
    <dialog
      ref={ref}
      // Deliberately no `onClose`. The native `close` event fires whenever close() is
      // called — including from this component's own unmount cleanup — so wiring the
      // parent's teardown to it made the modal destroy itself: React's StrictMode runs
      // every effect twice in development (mount, clean up, mount again), the cleanup
      // called close(), and the resulting event cleared the parent's state. The form
      // appeared and vanished in the same blink. Firefox showed it and Chromium did not,
      // because Chromium did not deliver the event — a difference that turns a real bug
      // into "works on my machine".
      //
      // Every way this modal can close is initiated by React below: the ✕ button, a
      // backdrop click, and Escape via `onCancel`. Nothing needs the native event.
      //
      // Escape fires `cancel`, and the browser would then close the dialog on its own.
      // That has to be intercepted, because the browser closing it is invisible to React:
      // the element ends up closed in the DOM while the parent still believes the modal is
      // open. The result is worse than it sounds — the form vanishes, and because the
      // parent's state never changed, pressing the button again sets the same value,
      // produces no re-render, and nothing reopens. The modal is then unopenable until the
      // page is reloaded, which looks exactly like a dead button.
      //
      // Cancelling the native close and routing through `onClose` keeps React the single
      // owner of whether this modal exists.
      onCancel={(e) => {
        e.preventDefault();
        onClose();
      }}
      // Clicking the backdrop closes; clicking the panel must not. A click on ::backdrop
      // reports the dialog itself as its target, which is how the two are told apart.
      //
      // The press has to have *started* on the backdrop as well, and that is not a detail.
      // The button that opens this modal sits outside the panel's box, so it is over the
      // backdrop of the dialog about to appear. Firefox delivers that same click to the
      // newly shown dialog, which read as a backdrop click and closed the modal in the
      // frame it opened — it appeared and vanished in a blink, while Chromium was fine.
      // The opening press landed on the button, before this dialog existed, so requiring a
      // press on the backdrop rejects it.
      //
      // It also fixes the everyday version of the same confusion: selecting text inside the
      // form and releasing the mouse outside it no longer throws away what you typed.
      onMouseDown={(e) => {
        pressStartedOnBackdrop.current = e.target === ref.current;
      }}
      onClick={(e) => {
        if (e.target === ref.current && pressStartedOnBackdrop.current) onClose();
        pressStartedOnBackdrop.current = false;
      }}
      aria-label={title}
      className={`m-auto w-[calc(100vw-2rem)] rounded-2xl border border-stone-200 bg-white p-0 text-stone-900 shadow-xl backdrop:bg-stone-900/40 ${SIZES[size]}`}
    >
      <div className="flex items-center justify-between border-b border-stone-100 px-5 py-3">
        <h2 className="text-sm font-semibold">{title}</h2>
        <button
          type="button"
          onClick={onClose}
          aria-label="Close"
          className="min-h-11 min-w-11 rounded-md text-stone-500 hover:bg-stone-100 hover:text-stone-900"
        >
          ✕
        </button>
      </div>
      <div className="max-h-[75vh] overflow-y-auto px-5 py-4">{children}</div>
      {footer && (
        <div className="border-t border-stone-100 px-5 py-3">{footer}</div>
      )}
    </dialog>
  );
}
