import React, { ReactNode, useEffect, useId, useRef } from 'react';
import { X } from 'lucide-react';
import IconButton from './IconButton';
import { cn } from '../../utils/classNames';

interface ModalShellProps {
  open: boolean;
  title: string;
  children: ReactNode;
  footer?: ReactNode;
  maxWidthClass?: string;
  onClose: () => void;
}

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

const ModalShell: React.FC<ModalShellProps> = ({
  open,
  title,
  children,
  footer,
  maxWidthClass = 'max-w-3xl',
  onClose,
}) => {
  const dialogRef = useRef<HTMLDivElement>(null);
  const titleId = useId();

  // Hold onClose in a ref so the focus-trap effect below depends only on `open`.
  // Otherwise a parent that passes a fresh onClose each render (common) would
  // re-run the effect on every keystroke, and its cleanup would steal focus from
  // whatever input the user is typing in.
  const onCloseRef = useRef(onClose);
  onCloseRef.current = onClose;

  // Escape to close, focus trap, and focus restore — applies to every modal that
  // uses ModalShell. Works inside the WordPress Shadow DOM (reads activeElement
  // from the dialog's root node).
  useEffect(() => {
    if (!open) return;
    const dialog = dialogRef.current;
    const root = (dialog?.getRootNode() as Document | ShadowRoot) ?? document;
    const previouslyFocused = root.activeElement as HTMLElement | null;

    const focusables = () =>
      dialog ? Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE)) : [];

    // Move focus into the dialog on open.
    (focusables()[0] ?? dialog)?.focus();

    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        event.stopPropagation();
        onCloseRef.current();
        return;
      }
      if (event.key !== 'Tab') return;
      const items = focusables();
      if (items.length === 0) {
        event.preventDefault();
        dialog?.focus();
        return;
      }
      const active = root.activeElement as HTMLElement | null;
      const index = active ? items.indexOf(active) : -1;
      if (event.shiftKey && (index <= 0)) {
        event.preventDefault();
        items[items.length - 1].focus();
      } else if (!event.shiftKey && index === items.length - 1) {
        event.preventDefault();
        items[0].focus();
      }
    };

    document.addEventListener('keydown', onKeyDown, true);
    return () => {
      document.removeEventListener('keydown', onKeyDown, true);
      previouslyFocused?.focus?.();
    };
  }, [open]);

  if (!open) return null;

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <button
        type="button"
        tabIndex={-1}
        aria-label="Close modal"
        className="absolute inset-0 bg-black/45 backdrop-blur-sm"
        onClick={onClose}
      />
      <div
        ref={dialogRef}
        role="dialog"
        aria-modal="true"
        aria-labelledby={titleId}
        tabIndex={-1}
        className={cn('relative z-10 flex max-h-[88vh] w-full flex-col overflow-hidden rounded-lg border border-app-border bg-app-surface shadow-2xl focus:outline-none', maxWidthClass)}
      >
        <div className="flex items-center justify-between border-b border-app-border px-5 py-4">
          <h2 id={titleId} className="text-base font-semibold text-app-text">{title}</h2>
          <IconButton label="Close" onClick={onClose}>
            <X size={18} />
          </IconButton>
        </div>
        <div className="min-h-0 flex-1 overflow-y-auto px-5 py-5 scrollbar-custom">
          {children}
        </div>
        {footer && (
          <div className="border-t border-app-border bg-app-surface-muted px-5 py-4">
            {footer}
          </div>
        )}
      </div>
    </div>
  );
};

export default ModalShell;
