import type { HTMLAttributes, ReactNode } from 'react';
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
import './modal.css';

export type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
export type ModalPosition = 'top' | 'center' | 'bottom';
type ModalTransitionState = 'enter' | 'entered' | 'exit';

export type ModalProps = {
  isOpen?: boolean;
  title?: ReactNode;
  size?: ModalSize;
  position?: ModalPosition;
  /** When true, only the modal body scrolls (header remains visible). */
  scrollable?: boolean;
  /** Smooth open/close duration (ms). */
  transitionDurationMs?: number;
  ariaLabel?: string;
  hideCloseButton?: boolean;
  disableCloseButton?: boolean;
  onClose?: () => void;
  children?: ReactNode;
} & Omit<HTMLAttributes<HTMLDivElement>, 'title'>;

export function Modal({
  isOpen = false,
  title,
  size = 'md',
  position = 'center',
  scrollable = false,
  transitionDurationMs = 320,
  ariaLabel,
  hideCloseButton = false,
  disableCloseButton = false,
  onClose,
  children,
  className,
  ...rest
}: ModalProps) {
  const modalId = useId().replace(/:/g, '');
  const titleId = `${modalId}-title`;
  const closeRef = useRef<HTMLButtonElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);
  const prevActive = useRef<HTMLElement | null>(null);
  const closeTimeout = useRef<number | null>(null);
  const [rendered, setRendered] = useState(isOpen);
  const [tState, setTState] = useState<ModalTransitionState>(isOpen ? 'entered' : 'exit');

  const hasTitle =
    title != null &&
    (typeof title === 'string' ? title.trim().length > 0 : true);

  const close = useCallback(() => onClose?.(), [onClose]);

  useEffect(() => {
    if (!isOpen) return;
    prevActive.current = document.activeElement as HTMLElement;
    const t = window.setTimeout(() => {
      if (closeRef.current) closeRef.current.focus();
      else contentRef.current?.focus();
    }, 0);
    return () => window.clearTimeout(t);
  }, [isOpen]);

  useEffect(() => {
    if (isOpen) return;
    prevActive.current?.focus();
    prevActive.current = null;
  }, [isOpen]);

  useEffect(() => {
    if (closeTimeout.current != null) {
      window.clearTimeout(closeTimeout.current);
      closeTimeout.current = null;
    }

    if (isOpen) {
      setRendered(true);
      setTState('enter');
      const raf = window.requestAnimationFrame(() => setTState('entered'));
      return () => window.cancelAnimationFrame(raf);
    }

    if (rendered) {
      setTState('exit');
      closeTimeout.current = window.setTimeout(() => {
        setRendered(false);
      }, transitionDurationMs);
    }
    return undefined;
  }, [isOpen, rendered, transitionDurationMs]);

  const onKeyDown = useCallback(
    (e: React.KeyboardEvent) => {
      if (e.key === 'Escape') close();
    },
    [close]
  );

  const onOverlayClick = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      if (e.target === e.currentTarget) close();
    },
    [close]
  );

  useEffect(() => {
    return () => {
      if (closeTimeout.current != null) window.clearTimeout(closeTimeout.current);
    };
  }, []);

  const overlayClassName = useMemo(() => {
    return [
      'modal-overlay',
      `modal-overlay--${position}`,
      `modal-overlay--${tState}`,
      className,
    ]
      .filter(Boolean)
      .join(' ');
  }, [className, position, tState]);

  const contentClassName = useMemo(() => {
    return [
      'modal-content',
      `modal-content--${size}`,
      scrollable ? 'modal-content--scrollable' : '',
      `modal-content--${tState}`,
    ]
      .filter(Boolean)
      .join(' ');
  }, [scrollable, size, tState]);

  if (!rendered) return null;

  return (
    <div
      className={overlayClassName}
      role="dialog"
      aria-modal="true"
      aria-labelledby={hasTitle ? titleId : undefined}
      aria-label={!hasTitle ? ariaLabel : undefined}
      tabIndex={0}
      onClick={onOverlayClick}
      onKeyDown={onKeyDown}
      {...rest}
    >
      <div
        ref={contentRef}
        className={contentClassName}
        style={
          {
            ['--ccl-modal-transition-ms' as never]: `${transitionDurationMs}ms`,
          } as React.CSSProperties
        }
        tabIndex={0}
        onClick={(e) => e.stopPropagation()}
        onKeyDown={onKeyDown}
      >
        <div className={`modal-header${!hasTitle ? ' modal-header--no-title' : ''}`}>
          {hasTitle ? (
            typeof title === 'string' ? (
              <h2 id={titleId} className="modal-title">
                {title}
              </h2>
            ) : (
              <div id={titleId} className="modal-title">
                {title}
              </div>
            )
          ) : null}
          {!hideCloseButton ? (
            <button
              ref={closeRef}
              type="button"
              className="modal-close"
              disabled={disableCloseButton}
              onClick={close}
              aria-label="Close modal"
            >
              <svg className="modal-close-icon" viewBox="0 0 16 16" aria-hidden focusable="false">
                <path
                  d="M2.146 2.146a.5.5 0 0 1 .708 0L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854a.5.5 0 0 1 0-.708z"
                  fill="currentColor"
                />
              </svg>
            </button>
          ) : null}
        </div>
        <div className="modal-body">{children}</div>
      </div>
    </div>
  );
}
