/* global React, Icon */
const { useEffect: useEffectM, useRef: useRefM } = React;

// ============================================================================
// <Modal /> — unified dialog component
// ----------------------------------------------------------------------------
// Drop-in replacement for the 13+ ad-hoc modal implementations across the
// codebase. Consolidates the boilerplate (backdrop, centering, head/body/foot
// layout, close button, Escape-to-close, body scroll lock, focus
// restoration) so callers only have to provide the actual content.
//
// Usage:
//
//   <Modal title="Nouvelle invitation"
//          onClose={onClose}
//          onSubmit={onSubmit}                       // optional → wraps as <form>
//          size="md"                                  // sm | md (default) | lg
//          footer={(<>
//            <button className="btn sm ghost" onClick={onClose}>Annuler</button>
//            <button type="submit" className="btn sm primary">Créer</button>
//          </>)}>
//     <label>…</label>
//     <input … />
//     …form fields…
//   </Modal>
//
// Behaviour:
//   • Backdrop click  → onClose()
//   • Escape key      → onClose() (single global listener per mount)
//   • Body scroll lock — page underneath can't scroll while modal is open;
//                       restored to whatever it was on unmount.
//   • Focus           — restored to the element that opened the modal when
//                       it closes (so the keyboard user returns where they
//                       were).
//   • Close button    — appears top-right of the header unless hideClose
//                       is true.
//   • Size variants   — 'sm' (380px) | 'md' (540px, default) | 'lg' (720px)
//                       map to the CSS classes .modal-sm / .modal-lg.
//   • flushBody       — if true, removes the default body padding so the
//                       caller can build edge-to-edge tables / canvases.
//
// Out of scope for this v1 (nice-to-have, can be added later):
//   • Strict focus trap (Tab cycling stays inside the modal).
//   • Stacked-modal handling (one modal opening another).
//   • Confirm-on-close when the form is dirty.
//
// CSS lives in styles.css under "===== Modals =====" (.modal-overlay,
// .modal, .modal-head, .modal-title, .modal-body, .modal-foot,
// .modal-sm, .modal-lg). Phase 1 added those styles to fix the
// scrolled-off-screen rendering bug; this Modal component is Phase 2 of
// the same refactor and reuses the same CSS so visual regression is zero.
// ============================================================================

function Modal({ title, onClose, onSubmit, footer, children, size, hideClose, flushBody, dialogClassName }) {
  const containerRef = useRefM(null);
  // Remember the element that had focus before the modal opened so we can
  // restore it on close. Improves keyboard UX (Tab order is preserved).
  const previousFocusRef = useRefM(null);
  useEffectM(() => {
    previousFocusRef.current = document.activeElement;
    return () => {
      const el = previousFocusRef.current;
      if (el && typeof el.focus === "function") {
        // Defer so React has finished unmounting before restoring focus
        // (avoids "Cannot focus an element on an unmounted tree" warnings).
        setTimeout(() => { try { el.focus(); } catch (_) {} }, 0);
      }
    };
  }, []);

  // Escape-to-close — single global listener bound to the mount.
  useEffectM(() => {
    const onKey = (e) => { if (e.key === "Escape" && onClose) onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onClose]);

  // Body scroll lock — the page underneath can't scroll while the modal is
  // open. Restored on unmount.
  useEffectM(() => {
    const original = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = original; };
  }, []);

  const sizeSuffix = size === "sm" ? " modal-sm" : size === "lg" ? " modal-lg" : "";
  const modalClass = "modal" + sizeSuffix + (dialogClassName ? " " + dialogClassName : "");

  // If onSubmit is provided, the inner container is a <form> so the
  // caller can use <button type="submit"> in the footer to trigger it.
  // Otherwise it's a plain <div> — the modal is presentational only.
  const InnerWrapper = onSubmit ? "form" : "div";
  const innerProps = onSubmit ? { onSubmit } : {};

  return (
    <div className="modal-overlay"
         onClick={onClose}
         role="dialog"
         aria-modal="true"
         aria-label={typeof title === "string" ? title : undefined}>
      <InnerWrapper
        ref={containerRef}
        className={modalClass}
        onClick={(e) => e.stopPropagation()}
        {...innerProps}>
        <div className="modal-head">
          <div className="modal-title">{title}</div>
          {!hideClose && (
            <button type="button" className="btn xs ghost" onClick={onClose}
                    aria-label="Close" title="Close">
              <Icon.x />
            </button>
          )}
        </div>
        <div className={"modal-body" + (flushBody ? " flush" : "")}>
          {children}
        </div>
        {footer && <div className="modal-foot">{footer}</div>}
      </InnerWrapper>
    </div>
  );
}

// Expose globally so legacy non-module screens can use it via `window.Modal`
// or just `Modal` (it's a global per Babel's eval in MELR.html).
window.Modal = Modal;
