import { useEffect, useCallback, type ReactNode } from 'react';
import { createPortal } from 'react-dom';
import { AnimatePresence, motion } from 'framer-motion';
import { X } from '@phosphor-icons/react';
import clsx from 'clsx';
import styles from './Modal.module.css';

// ---------------------------------------------------------------------------
// Subcomponent types
// ---------------------------------------------------------------------------

type ModalSize = 'small' | 'medium' | 'large';

interface ModalRootProps {
	isOpen: boolean;
	onClose: () => void;
	size?: ModalSize;
	children: ReactNode;
	className?: string;
	/**
	 * Optional z-index override for the backdrop + layer. Needed
	 * when the modal is opened on top of another portaled surface
	 * that already claims a high z-index (e.g. the ImageLightbox
	 * backdrop at 16000). Leave unset to inherit the global
	 * `--z-index-modal` token.
	 */
	zIndex?: number;
}

interface ModalHeaderProps {
	title: string;
	onClose?: () => void;
	children?: ReactNode;
	className?: string;
}

interface ModalContentProps {
	children: ReactNode;
	className?: string;
}

interface ModalFooterProps {
	children: ReactNode;
	className?: string;
}

// ---------------------------------------------------------------------------
// Modal.Root  --  Portal + backdrop + centred layer + surface
// ---------------------------------------------------------------------------

function ModalRoot({ isOpen, onClose, size = 'small', children, className, zIndex }: ModalRootProps) {
	const handleEscape = useCallback(
		(e: KeyboardEvent) => {
			if (e.key === 'Escape') onClose();
		},
		[onClose],
	);

	useEffect(() => {
		if (!isOpen) return;
		document.addEventListener('keydown', handleEscape);
		return () => document.removeEventListener('keydown', handleEscape);
	}, [isOpen, handleEscape]);

	return createPortal(
		<AnimatePresence>
			{isOpen && (
				<>
					{/* Backdrop — purely decorative. The click-to-close
					    handler lives on `.layer` below because `.layer`
					    is a sibling that paints on top of the backdrop
					    (both `position: fixed; inset: 0`), so clicks
					    never actually reach the backdrop in practice. */}
					<motion.div
						className={styles.backdrop}
						initial={{ opacity: 0 }}
						animate={{ opacity: 1 }}
						exit={{ opacity: 0 }}
						style={zIndex !== undefined ? { zIndex } : undefined}
					/>

					{/* Centring layer — click-to-close target for empty
					    space around the modal body. The inner
					    `motion.div` calls `stopPropagation` so clicks
					    inside the modal content don't bubble up here. */}
					<div
						className={styles.layer}
						style={zIndex !== undefined ? { zIndex } : undefined}
						onClick={onClose}
					>
						<motion.div
							className={clsx(styles.root, styles[size], className)}
							initial={{ opacity: 0, scale: 0.95, y: 10 }}
							animate={{ opacity: 1, scale: 1, y: 0 }}
							exit={{ opacity: 0, scale: 0.95, y: 10 }}
							transition={{ duration: 0.15 }}
							onClick={(e) => e.stopPropagation()}
						>
							{children}
						</motion.div>
					</div>
				</>
			)}
		</AnimatePresence>,
		document.body,
	);
}

// ---------------------------------------------------------------------------
// Modal.Header
// ---------------------------------------------------------------------------

function ModalHeader({ title, onClose, children, className }: ModalHeaderProps) {
	return (
		<div className={clsx(styles.header, className)}>
			<h2 className={styles.headerTitle}>{title}</h2>
			{children}
			{onClose && (
				<button
					type="button"
					className={styles.closeButton}
					onClick={onClose}
					aria-label="Close"
				>
					<X size={18} weight="bold" />
				</button>
			)}
		</div>
	);
}

// ---------------------------------------------------------------------------
// Modal.Content
// ---------------------------------------------------------------------------

function ModalContent({ children, className }: ModalContentProps) {
	return <div className={clsx(styles.content, className)}>{children}</div>;
}

// ---------------------------------------------------------------------------
// Modal.Footer
// ---------------------------------------------------------------------------

function ModalFooter({ children, className }: ModalFooterProps) {
	return <div className={clsx(styles.footer, className)}>{children}</div>;
}

// ---------------------------------------------------------------------------
// Backward-compatible default export
// ---------------------------------------------------------------------------

export interface ModalProps {
	isOpen: boolean;
	onClose: () => void;
	children: ReactNode;
	title?: string;
	/** @deprecated Use `size` instead. Kept for backward compatibility. */
	width?: number;
	size?: ModalSize;
	className?: string;
}

function sizeFromWidth(width: number | undefined): ModalSize {
	if (width === undefined) return 'small';
	if (width >= 800) return 'large';
	if (width >= 600) return 'medium';
	return 'small';
}

function ModalCompat({ isOpen, onClose, children, title, width, size, className }: ModalProps) {
	const resolvedSize = size ?? sizeFromWidth(width);

	return (
		<ModalRoot isOpen={isOpen} onClose={onClose} size={resolvedSize} className={className}>
			{title && <ModalHeader title={title} onClose={onClose} />}
			<ModalContent>{children}</ModalContent>
		</ModalRoot>
	);
}

// ---------------------------------------------------------------------------
// Compose the public API:  Modal  +  Modal.Root / Header / Content / Footer
// ---------------------------------------------------------------------------

export const Modal = Object.assign(ModalCompat, {
	Root: ModalRoot,
	Header: ModalHeader,
	Content: ModalContent,
	Footer: ModalFooter,
});
