/* ============================================================================
 * 서명전에 — TDS Modal 컴포넌트
 *  · 사용자의 주의가 필요한 중요한 내용을 화면 위에 띄워요. 다른 화면을
 *    덮고 사용자가 확인/액션을 끝내야만 돌아갈 수 있어요.
 *  · 2 서브:
 *      Modal.Overlay  — 뒤에 깔리는 dim. role="button" 으로 클릭 가능 (onClick).
 *      Modal.Content  — 중앙에 뜨는 카드. 자동 포커스(tabIndex=0).
 *
 *  · ReactDOM.createPortal 로 portalContainer (기본 document.body) 아래 렌더.
 *  · open 이 true 가 되면 mount + 다음 프레임에서 entered 클래스 부여 → CSS
 *    transition 으로 페이드/슬라이드 인.
 *  · open 이 false 가 되면 entered 클래스를 떼고, content transitionend 후
 *    실제 unmount + onExited() 호출. 안전장치로 setTimeout 폴백 (300ms).
 *
 *  · 외부 콘텐츠 aria-hidden:
 *      열림 시 portalContainer 의 직속 자식들 중 우리 portal node 가 아닌 것들에
 *      aria-hidden="true" 부여. 닫힘 시 원상복구.
 *
 *  · ESC 닫힘은 스펙에 명시 안 됐지만 모달 표준 — 추가 (closeOnEscape, 기본 true).
 *
 *  · MODAL_CONTEXT 로 Overlay/Content 가 부모 Modal 의 상태를 공유해요.
 * ========================================================================== */

const __ModalContext = React.createContext({
  isMounted: false,
  isEntered: false,
  contentRef: { current: null },
  registerOverlay: function () {},
});

// ---------- Overlay ----------
function ModalOverlay({ onClick, className = "", style, ...rest }) {
  const ctx = React.useContext(__ModalContext);

  const handleClick = function (e) {
    if (typeof onClick === "function") onClick(e);
  };

  const cls = ["tds-modal__overlay", className].filter(Boolean).join(" ");

  // role="button" + tabIndex=-1 (overlay 는 키보드 포커스 대상이 아니라
  // 마우스 클릭 보조용. 키보드 사용자는 ESC 또는 안의 닫기 버튼 사용)
  return (
    <div
      role="button"
      tabIndex={-1}
      aria-label="닫기"
      className={cls}
      style={style}
      onClick={handleClick}
      {...rest}
    />
  );
}

// ---------- Content ----------
function ModalContent({ children, className = "", style, ...rest }) {
  const ctx = React.useContext(__ModalContext);

  // mount 시 자동 포커스 — tabIndex={0} 으로 키보드 포커스 받을 수 있게
  const localRef = React.useRef(null);
  const setRef = function (el) {
    localRef.current = el;
    if (ctx && ctx.contentRef) ctx.contentRef.current = el;
  };

  React.useEffect(function () {
    if (!localRef.current) return undefined;
    // 다음 틱에 포커스 (애니메이션 시작 직후)
    const id = window.setTimeout(function () {
      if (localRef.current && typeof localRef.current.focus === "function") {
        try { localRef.current.focus({ preventScroll: true }); } catch (_) { localRef.current.focus(); }
      }
    }, 0);
    return function () { window.clearTimeout(id); };
  }, []);

  const cls = ["tds-modal__content", className].filter(Boolean).join(" ");

  return (
    <div
      ref={setRef}
      className={cls}
      style={style}
      tabIndex={0}
      role="dialog"
      aria-modal="true"
      {...rest}
    >
      {children}
    </div>
  );
}

// ---------- Modal (root, Portal + 상태) ----------
function Modal({
  open,
  onOpenChange,
  onExited,
  portalContainer,
  closeOnEscape = true,
  children,
}) {
  // mount 는 open 이 true 가 되는 순간 즉시, false 가 된 후엔 transition
  // 끝날 때까지 유지. entered 는 시각 상태 (CSS 클래스 토글).
  const [isMounted, setIsMounted] = React.useState(!!open);
  const [isEntered, setIsEntered] = React.useState(false);

  const contentRef = React.useRef(null);
  const exitTimerRef = React.useRef(null);

  const container =
    portalContainer && typeof portalContainer.appendChild === "function"
      ? portalContainer
      : (typeof document !== "undefined" ? document.body : null);

  // open 변화 처리
  React.useEffect(function () {
    if (open) {
      // mount → 다음 프레임에 entered = true (transition 트리거)
      if (exitTimerRef.current) {
        window.clearTimeout(exitTimerRef.current);
        exitTimerRef.current = null;
      }
      setIsMounted(true);
      // requestAnimationFrame 두 번 — 마운트 직후 첫 페인트를 기다린 뒤 클래스 토글
      let raf2 = 0;
      const raf1 = window.requestAnimationFrame(function () {
        raf2 = window.requestAnimationFrame(function () { setIsEntered(true); });
      });
      return function () {
        window.cancelAnimationFrame(raf1);
        if (raf2) window.cancelAnimationFrame(raf2);
      };
    }
    // close path — entered 떼고 transition 끝나면 unmount + onExited
    if (!isMounted) return undefined;
    setIsEntered(false);
    // 안전장치: 300ms 안에 transitionend 가 안 와도 강제 unmount
    if (exitTimerRef.current) window.clearTimeout(exitTimerRef.current);
    exitTimerRef.current = window.setTimeout(function () {
      setIsMounted(false);
      if (typeof onExited === "function") onExited();
      exitTimerRef.current = null;
    }, 300);
    return function () {
      // open 이 다시 true 로 바뀌면 이 effect 가 cleanup 됨 — 타이머 정리
      if (exitTimerRef.current) {
        window.clearTimeout(exitTimerRef.current);
        exitTimerRef.current = null;
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  // body 스크롤 락 — 마운트되어 있는 동안만
  React.useEffect(function () {
    if (!isMounted || typeof document === "undefined") return undefined;
    const root = document.documentElement;
    root.classList.add("tds-modal-open");
    return function () { root.classList.remove("tds-modal-open"); };
  }, [isMounted]);

  // 외부 콘텐츠 aria-hidden — portalContainer 의 직속 자식들 중 우리 portal 외 모두
  React.useEffect(function () {
    if (!isMounted || !container) return undefined;
    // 우리 portal 의 부모 div 를 식별하기 위해 마커 — container 에 추가될 마지막 자식
    const portalNodes = Array.prototype.filter.call(
      container.children,
      function (n) { return n && n.classList && n.classList.contains("tds-modal-portal"); }
    );
    const ourPortal = portalNodes[portalNodes.length - 1];
    const siblings = Array.prototype.filter.call(
      container.children,
      function (n) { return n !== ourPortal; }
    );
    const restore = [];
    siblings.forEach(function (el) {
      if (!el || !el.setAttribute) return;
      restore.push({ el: el, prev: el.getAttribute("aria-hidden") });
      el.setAttribute("aria-hidden", "true");
    });
    return function () {
      restore.forEach(function (r) {
        if (r.prev == null) r.el.removeAttribute("aria-hidden");
        else r.el.setAttribute("aria-hidden", r.prev);
      });
    };
  }, [isMounted, container]);

  // ESC 닫기
  React.useEffect(function () {
    if (!isMounted || !closeOnEscape) return undefined;
    const onKey = function (e) {
      if (e.key === "Escape" || e.key === "Esc") {
        e.stopPropagation();
        if (typeof onOpenChange === "function") onOpenChange(false);
      }
    };
    document.addEventListener("keydown", onKey);
    return function () { document.removeEventListener("keydown", onKey); };
  }, [isMounted, closeOnEscape, onOpenChange]);

  // content transitionend 로 unmount 트리거 — 안전장치 setTimeout 보다 먼저 도착
  // 이 시점에 onExited 도 호출. 단 entered 가 false 일 때만 (퇴장 transition).
  const onPortalTransitionEnd = function (e) {
    if (isEntered) return;
    // overlay/content 양쪽 transition 이 끝나는데, opacity 만 보면 충분
    if (e && e.propertyName && e.propertyName !== "opacity") return;
    if (exitTimerRef.current) {
      window.clearTimeout(exitTimerRef.current);
      exitTimerRef.current = null;
    }
    setIsMounted(false);
    if (typeof onExited === "function") onExited();
  };

  if (!isMounted || !container) return null;

  // 자동으로 Modal.Overlay / Modal.Content 가 둘 다 들어 있지 않은 경우
  // (예: <Modal>{children only}</Modal>) 도 렌더는 시도해 보되, 권장은 둘 다.
  const cls = ["tds-modal", isEntered ? "tds-modal--entered" : ""].filter(Boolean).join(" ");

  return ReactDOM.createPortal(
    <__ModalContext.Provider
      value={{ isMounted: isMounted, isEntered: isEntered, contentRef: contentRef }}
    >
      <div
        className={`tds-modal-portal ${cls}`}
        onTransitionEnd={onPortalTransitionEnd}
      >
        {children}
      </div>
    </__ModalContext.Provider>,
    container
  );
}

Modal.Overlay = ModalOverlay;
Modal.Content = ModalContent;

window.Modal = Modal;
window.ModalOverlay = ModalOverlay;
window.ModalContent = ModalContent;

Object.assign(window, { Modal, ModalOverlay, ModalContent });
