/* ============================================================================
 * 서명전에 — TDS ConfirmDialog 컴포넌트 (Task #162)
 *
 *  · TDS Mobile 2026-03 ConfirmDialog 스펙. 사용자의 액션이나 선택이 필요한
 *    상황에서 취소/확인 두 개의 버튼을 통해 명확한 선택을 돕는 다이얼로그.
 *
 *  · 구성:
 *      ConfirmDialog.Title         — 제목 (기본 <h3> + t4 + bold + grey-800)
 *      ConfirmDialog.Description   — 설명 (기본 <h3> + t6 + medium + grey-600)
 *      ConfirmDialog.CancelButton  — 취소 버튼 (보조)
 *      ConfirmDialog.ConfirmButton — 확인 버튼 (주요)
 *
 *  · props (root):
 *      open*                 boolean
 *      title                 React.ReactNode — <ConfirmDialog.Title> 권장
 *      description           React.ReactNode — 없어도 됨
 *      cancelButton*         React.ReactNode — <ConfirmDialog.CancelButton> 권장
 *      confirmButton*        React.ReactNode — <ConfirmDialog.ConfirmButton> 권장
 *      buttonOrder           "cancel-first" | "confirm-first" (기본 "cancel-first")
 *                             — iOS/Android 표준은 cancel-first(좌) 순서.
 *      forceVerticalButtons  boolean (기본 false) — 버튼 길이와 무관하게 세로 강제
 *      closeOnDimmerClick    boolean (기본 true) — false 면 딤 클릭 시 Wiggle
 *      closeOnBackEvent      boolean (기본 true) — 뒤로가기 시 닫힘 (popstate)
 *      onClose               () => void  — 딤/뒤로가기/ESC/취소 모두에서 호출 (CTA 버튼은 호출자가 onClose 추가 호출)
 *      onEntered             () => void
 *      onExited              () => void
 *      portalContainer       HTMLElement (기본 document.body)
 *
 *  · 자동 레이아웃 — 기본은 가로 1:1 배치, useLayoutEffect 로 버튼 텍스트가 줄바꿈
 *    (offsetHeight > singleLineHeight * 1.5) 되었는지 측정해 세로로 전환.
 *    forceVerticalButtons=true 면 측정 생략하고 즉시 세로.
 *
 *  · AlertDialog(#161) 와 같은 portal/wiggle/popstate 인프라. z-index 1960/2060
 *    (AlertDialog 1950/2050 위에 살짝 — 단순 안내가 떠 있는 상태에서도 결정 다이얼로그를 위로).
 *
 *  · Modal(#137) 과 역할 구분:
 *      자유 레이아웃 다이얼로그 = Modal / 제목+설명+1버튼 = AlertDialog / 취소+확인 = ConfirmDialog.
 *
 *  · 모든 색/폰트/간격은 var(--tds-…) 토큰. 하드코딩 없음.
 *  · prefers-reduced-motion 시 모든 transition/애니메이션 OFF.
 *  · aria-modal="true" + role="alertdialog" + aria-labelledby/aria-describedby 자동.
 * ========================================================================== */

// ─── Title ───────────────────────────────────────────────────────────
function ConfirmDialogTitle({
  as = "h3",
  color,
  typography = "t4",
  fontWeight = "bold",
  className = "",
  style,
  children,
  ...rest
}) {
  const Tag = as || "h3";
  const cls = [
    "tds-confirmdialog__title",
    "tds-confirmdialog__title--" + typography,
    "tds-confirmdialog__title--w-" + fontWeight,
    className,
  ]
    .filter(Boolean)
    .join(" ");
  const mergedStyle = Object.assign({}, style || {}, color ? { color: color } : {});
  return (
    <Tag className={cls} style={mergedStyle} {...rest}>
      {children}
    </Tag>
  );
}

// ─── Description ─────────────────────────────────────────────────────
function ConfirmDialogDescription({
  as = "h3",
  color,
  typography = "t6",
  fontWeight = "medium",
  className = "",
  style,
  children,
  ...rest
}) {
  const Tag = as || "h3";
  const cls = [
    "tds-confirmdialog__description",
    "tds-confirmdialog__description--" + typography,
    "tds-confirmdialog__description--w-" + fontWeight,
    className,
  ]
    .filter(Boolean)
    .join(" ");
  const mergedStyle = Object.assign({}, style || {}, color ? { color: color } : {});
  return (
    <Tag className={cls} style={mergedStyle} {...rest}>
      {children}
    </Tag>
  );
}

// ─── 공통 버튼 베이스 ────────────────────────────────────────────────
// TextButton(#153) 위임 + native <button> 폴백.
function _confirmButtonBase(extraCls, fallbackColor, defaults, props) {
  const {
    size,
    color,
    fontWeight,
    variant,
    className = "",
    style,
    children,
    onClick,
    ...rest
  } = props;
  const _size = size || defaults.size;
  const _color = color || defaults.color;
  const _fw = fontWeight || defaults.fontWeight;
  const _variant = variant || defaults.variant;

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

  if (typeof window !== "undefined" && typeof window.TextButton === "function") {
    return React.createElement(
      window.TextButton,
      Object.assign(
        {
          size: _size,
          variant: _variant,
          className: cls,
          style: style,
          onClick: onClick,
          color: _color,
          fontWeight: _fw,
        },
        rest
      ),
      children
    );
  }

  // 폴백 — TextButton 미로드 환경
  const fallbackCls = [cls, "tds-confirmdialog__btn--fallback"].filter(Boolean).join(" ");
  const mergedStyle = Object.assign(
    {
      color: _color || fallbackColor,
      fontWeight:
        _fw === "regular" ? 400
          : _fw === "medium" ? 500
          : _fw === "semibold" ? 600
          : 700,
    },
    style || {}
  );
  return (
    <button
      type="button"
      className={fallbackCls}
      style={mergedStyle}
      onClick={onClick}
      {...rest}
    >
      {children}
    </button>
  );
}

// ─── CancelButton ────────────────────────────────────────────────────
function ConfirmDialogCancelButton(props) {
  return _confirmButtonBase(
    "tds-confirmdialog__btn--cancel",
    "var(--tds-grey-600)",
    { size: "medium", color: undefined, fontWeight: "medium", variant: "clear" },
    props
  );
}

// ─── ConfirmButton ───────────────────────────────────────────────────
function ConfirmDialogConfirmButton(props) {
  return _confirmButtonBase(
    "tds-confirmdialog__btn--confirm",
    "var(--tds-blue-500)",
    { size: "medium", color: undefined, fontWeight: "bold", variant: "clear" },
    props
  );
}

// ─── 루트 ConfirmDialog ──────────────────────────────────────────────
function ConfirmDialog({
  open,
  title,
  description,
  cancelButton,
  confirmButton,
  buttonOrder = "cancel-first",
  forceVerticalButtons = false,
  closeOnDimmerClick = true,
  closeOnBackEvent = true,
  onClose,
  onEntered,
  onExited,
  portalContainer,
  className = "",
  ...rest
}) {
  const [isMounted, setIsMounted] = React.useState(!!open);
  const [isEntered, setIsEntered] = React.useState(false);
  const [wiggle, setWiggle] = React.useState(0);
  const [vertical, setVertical] = React.useState(!!forceVerticalButtons);

  const exitTimerRef = React.useRef(null);
  const wiggleTimerRef = React.useRef(null);
  const contentRef = React.useRef(null);
  const buttonRowRef = React.useRef(null);
  const enteredCalledRef = React.useRef(false);

  // 고유 id — aria 페어링
  const uidRef = React.useRef(null);
  if (uidRef.current == null) {
    uidRef.current = "tds-confirmdialog-" + Math.random().toString(36).slice(2, 10);
  }
  const uid = uidRef.current;

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

  // ── open 변화: mount + enter, unmount + exit ──
  React.useEffect(function () {
    if (open) {
      if (exitTimerRef.current) {
        window.clearTimeout(exitTimerRef.current);
        exitTimerRef.current = null;
      }
      setIsMounted(true);
      enteredCalledRef.current = false;
      let raf2 = 0;
      const raf1 = window.requestAnimationFrame(function () {
        raf2 = window.requestAnimationFrame(function () { setIsEntered(true); });
      });
      return function () {
        window.cancelAnimationFrame(raf1);
        if (raf2) window.cancelAnimationFrame(raf2);
      };
    }
    if (!isMounted) return undefined;
    setIsEntered(false);
    if (exitTimerRef.current) window.clearTimeout(exitTimerRef.current);
    exitTimerRef.current = window.setTimeout(function () {
      setIsMounted(false);
      if (typeof onExited === "function") {
        try { onExited(); } catch (_) {}
      }
      exitTimerRef.current = null;
    }, 300);
    return function () {
      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-confirmdialog-open");
    return function () { root.classList.remove("tds-confirmdialog-open"); };
  }, [isMounted]);

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

  // ── popstate (뒤로가기) ──
  React.useEffect(function () {
    if (!isMounted || !closeOnBackEvent) return undefined;
    const marker = { __tds_confirmdialog: uid };
    try { window.history.pushState(marker, ""); } catch (_) {}
    const onPop = function () {
      if (typeof onClose === "function") onClose();
    };
    window.addEventListener("popstate", onPop);
    return function () {
      window.removeEventListener("popstate", onPop);
      try {
        if (window.history.state && window.history.state.__tds_confirmdialog === uid) {
          window.history.back();
        }
      } catch (_) {}
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isMounted, closeOnBackEvent, uid]);

  // ── transitionend → onEntered / onExited ──
  const onTransitionEnd = function (e) {
    if (!e || !e.propertyName) return;
    if (e.target !== contentRef.current) return;
    if (e.propertyName !== "opacity" && e.propertyName !== "transform") return;
    if (isEntered && !enteredCalledRef.current) {
      enteredCalledRef.current = true;
      if (typeof onEntered === "function") {
        try { onEntered(); } catch (_) {}
      }
      return;
    }
    if (!isEntered) {
      if (exitTimerRef.current) {
        window.clearTimeout(exitTimerRef.current);
        exitTimerRef.current = null;
      }
      setIsMounted(false);
      if (typeof onExited === "function") {
        try { onExited(); } catch (_) {}
      }
    }
  };

  // ── 딤(overlay) 클릭 ──
  const onOverlayClick = function () {
    if (closeOnDimmerClick) {
      if (typeof onClose === "function") onClose();
      return;
    }
    if (wiggleTimerRef.current) window.clearTimeout(wiggleTimerRef.current);
    setWiggle(function (n) { return n + 1; });
    wiggleTimerRef.current = window.setTimeout(function () {
      setWiggle(0);
      wiggleTimerRef.current = null;
    }, 420);
  };

  React.useEffect(function () {
    return function () {
      if (wiggleTimerRef.current) {
        window.clearTimeout(wiggleTimerRef.current);
        wiggleTimerRef.current = null;
      }
    };
  }, []);

  // ── 자동 레이아웃 측정 — 가로 → 세로 전환 ──
  // 가로 모드일 때 버튼 중 하나라도 두 줄(또는 원래 한 줄 높이의 1.5배) 이상으로
  // 줄바꿈됐다면 세로로 전환. forceVerticalButtons 면 즉시 세로.
  React.useLayoutEffect(function () {
    if (!isMounted) return;
    if (forceVerticalButtons) {
      if (!vertical) setVertical(true);
      return;
    }
    // 가로 모드에서만 측정. 일단 가로를 한 번 그려본 뒤 측정 → 세로 전환.
    if (vertical) return;
    const row = buttonRowRef.current;
    if (!row) return;
    const buttons = row.querySelectorAll(".tds-confirmdialog__btn");
    if (!buttons || buttons.length === 0) return;
    let needsVertical = false;
    for (let i = 0; i < buttons.length; i += 1) {
      const b = buttons[i];
      // 한 줄 기준 line-height (computed style) — 폴백 22 (sub-ty-5 lh)
      const cs = window.getComputedStyle(b);
      let lh = parseFloat(cs.lineHeight);
      if (!isFinite(lh) || lh <= 0) lh = 22;
      // offsetHeight 가 line-height 의 1.5배 이상이면 줄바꿈된 것으로 간주.
      // 패딩 등 다른 영향 보정용 임계는 살짝 보수적으로(1.45).
      if (b.offsetHeight > lh * 1.45) {
        needsVertical = true;
        break;
      }
      // 가로 폭이 컨테이너의 1/2 - gap 보다 큰 경우(scrollWidth > clientWidth + 1)도
      // overflow → 세로 권장.
      if (b.scrollWidth > b.clientWidth + 1) {
        needsVertical = true;
        break;
      }
    }
    if (needsVertical) setVertical(true);
    // open/forceVerticalButtons/wiggle/buttons 변화 시 재측정
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isMounted, forceVerticalButtons, vertical, cancelButton, confirmButton, wiggle]);

  // open 이 false 로 닫힐 때 vertical 초기화 (다음 열림에서 다시 측정)
  React.useEffect(function () {
    if (!open) {
      setVertical(!!forceVerticalButtons);
    }
  }, [open, forceVerticalButtons]);

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

  const rootCls = [
    "tds-confirmdialog-portal",
    "tds-confirmdialog",
    isEntered && "tds-confirmdialog--entered",
    wiggle > 0 && "tds-confirmdialog--wiggle",
    className,
  ]
    .filter(Boolean)
    .join(" ");

  // aria-labelledby / aria-describedby
  const titleId = title ? uid + "-title" : undefined;
  const descId = description ? uid + "-desc" : undefined;

  const titleNode = title && React.isValidElement(title)
    ? React.cloneElement(title, Object.assign({}, { id: title.props && title.props.id ? title.props.id : titleId }))
    : title;
  const descNode = description && React.isValidElement(description)
    ? React.cloneElement(description, Object.assign({}, { id: description.props && description.props.id ? description.props.id : descId }))
    : description;

  // 버튼 정렬 — 기본 "cancel-first" (좌측 cancel, 우측 confirm)
  const left = buttonOrder === "confirm-first" ? confirmButton : cancelButton;
  const right = buttonOrder === "confirm-first" ? cancelButton : confirmButton;

  const buttonRowCls = [
    "tds-confirmdialog__button-row",
    vertical ? "tds-confirmdialog__button-row--vertical" : "tds-confirmdialog__button-row--horizontal",
  ].join(" ");

  return ReactDOM.createPortal(
    <div className={rootCls} {...rest}>
      <div
        className="tds-confirmdialog__overlay"
        role="button"
        tabIndex={-1}
        aria-label="닫기"
        onClick={onOverlayClick}
      />
      <div
        ref={contentRef}
        className="tds-confirmdialog__content"
        role="alertdialog"
        aria-modal="true"
        aria-labelledby={titleId}
        aria-describedby={descId}
        tabIndex={0}
        onTransitionEnd={onTransitionEnd}
        // wiggle 재시작용 key — 같은 클릭 시 애니메이션을 처음부터 재생
        key={"wiggle-" + wiggle}
      >
        {titleNode ? <div className="tds-confirmdialog__title-slot">{titleNode}</div> : null}
        {descNode ? <div className="tds-confirmdialog__description-slot">{descNode}</div> : null}
        {(cancelButton || confirmButton) ? (
          <div ref={buttonRowRef} className={buttonRowCls}>
            {left ? <div className="tds-confirmdialog__button-cell">{left}</div> : null}
            {right ? <div className="tds-confirmdialog__button-cell">{right}</div> : null}
          </div>
        ) : null}
      </div>
    </div>,
    container
  );
}

// 서브 정적 부착
ConfirmDialog.Title = ConfirmDialogTitle;
ConfirmDialog.Description = ConfirmDialogDescription;
ConfirmDialog.CancelButton = ConfirmDialogCancelButton;
ConfirmDialog.ConfirmButton = ConfirmDialogConfirmButton;

window.ConfirmDialog = ConfirmDialog;
window.ConfirmDialogTitle = ConfirmDialogTitle;
window.ConfirmDialogDescription = ConfirmDialogDescription;
window.ConfirmDialogCancelButton = ConfirmDialogCancelButton;
window.ConfirmDialogConfirmButton = ConfirmDialogConfirmButton;

Object.assign(window, {
  ConfirmDialog,
  ConfirmDialogTitle,
  ConfirmDialogDescription,
  ConfirmDialogCancelButton,
  ConfirmDialogConfirmButton,
});
