"use client";

import { useEffect, useRef } from "react";
import { createPortal } from "react-dom";

interface Props {
  open: boolean;
  /** Called when user clicks the backdrop or presses Escape. */
  onClose: () => void;
  children: React.ReactNode;
  /**
   * ARIA label fallback when no labelled element exists inside.
   * Optional; if you have a heading inside the modal with id, set
   * `aria-labelledby` on a wrapper instead.
   */
  ariaLabel?: string;
}

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

/**
 * Portal-based modal.
 *
 * Why a portal
 * ------------
 * `position: fixed` becomes positioned relative to a transformed
 * ancestor's containing block, not the viewport, when ANY ancestor
 * has a `transform`, `perspective`, or `filter` applied. Our
 * `animate-in` utility sets `transform: translateY(0)` on a lot of
 * dashboard/tenant-detail containers, which broke modals rendered as
 * in-place children — they centred to the panel they lived in, not to
 * the page. Rendering at `document.body` via `createPortal` escapes
 * every containing-block ancestor and gives us true viewport coords.
 *
 * UX / a11y details
 * -----------------
 * - Backdrop click triggers `onClose` (only when the click target IS
 *   the backdrop, not the panel inside).
 * - Escape triggers `onClose`.
 * - `body` overflow is locked while open so background content doesn't
 *   scroll behind the modal.
 * - Focus is moved into the panel on open, trapped within it while open
 *   (Tab / Shift+Tab cycle), and restored to the previously focused
 *   element on close — so keyboard and screen-reader users can't tab
 *   out to the inert page behind the dialog.
 */
export function Modal({ open, onClose, children, ariaLabel }: Props) {
  const closeRef = useRef(onClose);
  closeRef.current = onClose;
  const panelRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;

    // Remember what had focus so we can restore it on close.
    const previouslyFocused = document.activeElement as HTMLElement | null;

    // Lock background scroll.
    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";

    // Move focus into the dialog — first focusable element, else the
    // panel itself (it carries tabIndex={-1}).
    const panel = panelRef.current;
    const focusables = panel
      ? Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE))
      : [];
    (focusables[0] ?? panel)?.focus();

    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        closeRef.current();
        return;
      }
      if (e.key !== "Tab" || !panel) return;

      // Re-query each time — modal content can change between tabs.
      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 first = items[0];
      const last = items[items.length - 1];
      const active = document.activeElement;

      if (e.shiftKey) {
        if (active === first || active === panel) {
          e.preventDefault();
          last.focus();
        }
      } else if (active === last) {
        e.preventDefault();
        first.focus();
      }
    };

    window.addEventListener("keydown", onKey);

    return () => {
      document.body.style.overflow = previousOverflow;
      window.removeEventListener("keydown", onKey);
      // Restore focus to the trigger (if it's still in the document).
      if (previouslyFocused && document.contains(previouslyFocused)) {
        previouslyFocused.focus();
      }
    };
  }, [open]);

  if (!open) return null;
  if (typeof document === "undefined") return null;

  return createPortal(
    <div
      className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm"
      onClick={(e) => {
        if (e.target === e.currentTarget) onClose();
      }}
    >
      <div
        ref={panelRef}
        role="dialog"
        aria-modal="true"
        aria-label={ariaLabel}
        tabIndex={-1}
        className="bg-surface-1 border border-border rounded-xl p-6 max-w-md w-full max-h-[90vh] overflow-y-auto focus:outline-none"
      >
        {children}
      </div>
    </div>,
    document.body
  );
}
