/**
 * Modal shell (docs/architecture/03 → Layer 3).
 *
 * Renders exactly ONE modal — whichever `activeModal` names — inside a single
 * reusable chrome: backdrop, header (title + close), and a focus trap. The body
 * comes from `MODAL_COMPONENTS` below — the only place a modal name maps to a
 * view, so there is no `name === 'datasets' && <DatasetsModal/>` chain anywhere.
 * The map lives here rather than in the registry so the registry (which the
 * coordinator and URL sync import) never imports components — that edge would
 * close an import cycle: coordinator → registry → component → coordinator.
 *
 * Dismissal is uniform for these passive feature modals: the close button,
 * Escape, or a backdrop click — never a click inside the body (which stops
 * propagation). Each modal owns its own action buttons; the shell stays generic.
 */

import type { ComponentType } from 'react';
import { useAppStore } from '../stores/AppStore';
import { getModalConfig, getModalTitle } from '../modals/modal-registry';
import type { ModalName } from '../modals/types';
import { closeModal } from '../modals/ModalCoordinator';
import { useFocusTrap } from '../hooks/useFocusTrap';
import { AboutModal } from './AboutModal';
import { ChartBuilderModal } from './ChartBuilderModal';
import { DatasetsModal } from './DatasetsModal';
import { DonateModal } from './DonateModal';
import { ExtractModal } from './ExtractModal';
import { ThemeBuilderModal } from './ThemeBuilderModal';
import { Icon } from './Icon';
import { IconButton } from './IconButton';
import styles from './ModalShell.module.css';

/** The body rendered for each modal name (metadata stays in the registry). */
const MODAL_COMPONENTS: Record<ModalName, ComponentType> = {
  datasets: DatasetsModal,
  extract: ExtractModal,
  chartBuilder: ChartBuilderModal,
  themeBuilder: ThemeBuilderModal,
  about: AboutModal,
  donate: DonateModal,
};

export function ModalShell() {
  const name = useAppStore((s) => s.activeModal);
  const config = getModalConfig(name);

  // The Chart Builder and Theme Builder are near-fullscreen work surfaces; the
  // Datasets manager is the standard large two-pane modal; everything else is a
  // small form. Both large kinds get the static-title initial focus (APG
  // dialog-modal) so content isn't skipped.
  const isXLarge = name === 'chartBuilder' || name === 'themeBuilder';
  const isLarge = name === 'datasets' || isXLarge;

  // Move focus into the modal on open, return it to the trigger on close. For a
  // large manager (list + detail), APG dialog-modal advises focusing a static
  // element at the top — the title — so the content's start is perceived rather
  // than jumping past it to the first control. Small form modals keep their
  // first-field focus (Extract autofocuses its name input).
  const modalRef = useFocusTrap<HTMLDivElement>(
    name !== null,
    isLarge ? '#modal-title' : undefined,
  );

  if (!config || !name) return null;
  const Body = MODAL_COMPONENTS[name];

  // Modals with in-progress work (the Chart Builder's config) opt out of
  // click-outside-to-close so an accidental backdrop click can't discard it; Escape
  // and the close button still dismiss. Other modals keep backdrop dismissal.
  const dismissOnBackdrop = config.dismissOnBackdrop !== false;
  const sizeClass = isXLarge ? styles.xlarge : isLarge ? styles.large : styles.small;

  return (
    <div
      className={styles.backdrop}
      onClick={dismissOnBackdrop ? () => void closeModal() : undefined}
      onKeyDown={(e) => {
        if (e.key === 'Escape') {
          e.stopPropagation();
          void closeModal();
        }
      }}
    >
      <div
        ref={modalRef}
        className={`${styles.modal} ${sizeClass}`}
        role="dialog"
        aria-modal="true"
        aria-labelledby="modal-title"
        onClick={(e) => e.stopPropagation()}
      >
        <header className={styles.header}>
          {/* tabIndex -1 makes the title a programmatic focus target for the
              large-manager initial focus (APG dialog-modal), without adding it to
              the Tab order. */}
          <h2 id="modal-title" className={styles.title} tabIndex={-1}>
            {getModalTitle(name)}
          </h2>
          <IconButton label="Close" onClick={() => void closeModal()}>
            <Icon name="close" />
          </IconButton>
        </header>
        <div className={styles.body}>
          <Body />
        </div>
      </div>
    </div>
  );
}
