"use client";

import { useEffect, useRef, type ReactNode } from "react";
import { cx } from "../../utils";

export type FoxModalSize = "sm" | "md" | "lg" | "full";

export interface FoxModalProps {
  /** 열림 여부. 호출부가 소유한다 — 모달이 스스로 닫지 않는다. */
  open: boolean;
  /** 닫기 버튼·Esc·바깥 클릭에서 부른다. */
  onClose: () => void;
  children?: ReactNode;
  size?: FoxModalSize;
  /** 넘기지 않으면 제목 줄을 그리지 않는다. */
  title?: ReactNode;
  /** 아래쪽 오른편. 보통 버튼 묶음이다. */
  actions?: ReactNode;
  /** 아래쪽 왼편. 시안은 "오늘 하루 보지 않기" 같은 체크박스 자리다. */
  utility?: ReactNode;
  /** 닫기 버튼을 감춘다. */
  closable?: boolean;
  closeLabel?: string;
  /** 바깥을 눌러도 닫히지 않게 한다. */
  dismissible?: boolean;
  /** 배치 조정용. */
  className?: string;
}

/**
 * @fox 모달. 네이티브 `<dialog>`를 쓴다 — 초점 가두기·Esc 닫기·뒷배경·맨 위 레이어를
 * 브라우저가 처리하므로 그걸 직접 구현하지 않는다.
 *
 * 열림 여부는 호출부가 소유한다. 사용자가 Esc를 누르거나 바깥을 눌러도 모달이 스스로
 * 닫지 않고 `onClose`만 부른다 — 닫을지 말지는 호출부가 정한다.
 *
 * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
 * (또는 개별 파티셜)로 한 번 불러와야 한다.
 */
export function FoxModal({
  open,
  onClose,
  children,
  size = "md",
  title,
  actions,
  utility,
  closable = true,
  closeLabel = "닫기",
  dismissible = true,
  className,
}: FoxModalProps) {
  const ref = useRef<HTMLDialogElement>(null);

  // `open` 속성만 세우면 뒷배경도 초점 가두기도 없는 그냥 상자가 된다. 모달로 띄우려면
  // `showModal()`을 불러야 한다.
  useEffect(() => {
    const dialog = ref.current;
    if (!dialog) {
      return;
    }
    if (open && !dialog.open) {
      dialog.showModal();
    } else if (!open && dialog.open) {
      dialog.close();
    }
  }, [open]);

  return (
    <dialog
      ref={ref}
      className={cx("fox-modal", `fox-modal--${size}`, className)}
      // Esc는 브라우저가 닫아 버리므로 막고, 닫을지는 호출부가 정하게 한다.
      onCancel={(event) => {
        // 파일 선택 취소도 cancel을 버블링시킨다 — 대상이 dialog일 때만 닫기로 본다.
        if (event.target !== event.currentTarget) {
          return;
        }
        event.preventDefault();
        onClose();
      }}
      onClick={(event) => {
        // `<dialog>`는 뒷배경을 눌러도 자기 자신이 대상이 된다 — 상자 밖을 눌렀는지는
        // 좌표로 가린다.
        if (!dismissible || event.target !== event.currentTarget) {
          return;
        }
        const box = event.currentTarget.getBoundingClientRect();
        const outside =
          event.clientX < box.left ||
          event.clientX > box.right ||
          event.clientY < box.top ||
          event.clientY > box.bottom;
        if (outside) {
          onClose();
        }
      }}
    >
      <div className="fox-modal__body">
        {/* 제목이 없어도 줄은 남긴다 — 그래야 닫기 버튼과 내용이 겹치지 않는다.
            빈 제목을 심으면 이름 없는 heading이 되므로 `<h2>`는 있을 때만 낸다. */}
        <div className="fox-modal__tit">{title && <h2 className="fox-modal__title">{title}</h2>}</div>
        <div className="fox-modal__contents">{children}</div>
      </div>

      {(utility || actions) && (
        <div className="fox-modal__foot">
          {utility && <div className="fox-modal__utility">{utility}</div>}
          {actions && <div className="fox-modal__actions">{actions}</div>}
        </div>
      )}

      {closable && (
        <button className="fox-modal__close" type="button" aria-label={closeLabel} onClick={onClose}>
          <span className="fox-modal__close-icon" aria-hidden="true">
            <i className="fox-ico fox-ico-X" aria-hidden="true" />
          </span>
        </button>
      )}
    </dialog>
  );
}
