/* ============================================================================
 * 서명전에 — TDS BottomSheet 컴포넌트
 *  · 화면 하단에서 슬라이드 업으로 나타나는 패널.
 *  · 서브 컴포넌트:
 *      - BottomSheet.Header              — 제목 (기본 t4)
 *      - BottomSheet.HeaderDescription   — 부제목 (기본 t6)
 *      - BottomSheet.CTA                 — 단일 주요 버튼
 *      - BottomSheet.DoubleCTA           — 좌우로 나뉜 2버튼
 *      - BottomSheet.Select              — 선택지(라디오) 목록
 *
 *  · props (TDS 스펙 기준):
 *      open*               — 열림 여부
 *      onClose             — 닫힘 요청 콜백 (ESC, 딤 클릭, 뒤로가기)
 *      header              — 제목 영역 (주로 BottomSheet.Header)
 *      headerDescription   — 부제목 영역
 *      cta                 — 하단 CTA 영역 (주로 BottomSheet.CTA/DoubleCTA)
 *      children            — 본문
 *      className           — 시트 영역 class
 *      dimmerClassName     — 딤 영역 class
 *      disableDimmer       — true 면 딤 제거
 *      hasTextField        — 내부에 키보드 포커스가 있을 때 키보드 위로 올림
 *      maxHeight           — 기본 높이 (px, 숫자)
 *      expandedMaxHeight   — 확장 상태 최대 높이 (px, 숫자)
 *      expandBottomSheet   — 드래그 확장 허용
 *      expandBottomSheetWhenScroll — 스크롤로도 확장
 *      ctaContentGap       — CTA 와 본문 사이 여백 (기본 34)
 *      ariaLabelledBy, ariaDescribedBy — 접근성 ID
 *      onEntered, onExited, onExpanded
 *      onDimmerClick
 *      onHandlerTouchStart, onHandlerTouchEnd
 *      portalContainer     — UNSAFE_disableFocusLock=true 이 아니면 document.body
 *      UNSAFE_disableFocusLock, UNSAFE_ignoreDimmerClick, UNSAFE_ignoreBackEvent
 *      a11yIncludeHeaderInScroll, disableChildrenDragging
 *
 *  · 스펙 중 모바일 제스처(드래그로 확장/닫기) 는 스캐폴드 상태.
 *    API(props + 콜백) 는 전부 수신하지만, 현재 포팅 단계에서는 maxHeight/
 *    expandedMaxHeight 를 기본값으로만 적용하고 그래버 터치 이벤트는
 *    onHandlerTouchStart/End 로 패스스루. 네이티브 wrapper 도입 시
 *    제스처 바인딩을 추가해요.
 * ========================================================================== */

function BottomSheet({
  open,
  onClose,
  header,
  headerDescription,
  cta,
  children,
  className = "",
  dimmerClassName = "",
  disableDimmer = false,
  hasTextField = false,
  maxHeight,
  expandedMaxHeight,
  expandBottomSheet = false,
  expandBottomSheetWhenScroll = false,
  ctaContentGap = 34,
  ariaLabelledBy,
  ariaDescribedBy,
  onEntered,
  onExited,
  onExpanded,
  onDimmerClick,
  onHandlerTouchStart,
  onHandlerTouchEnd,
  UNSAFE_disableFocusLock = false,
  UNSAFE_ignoreDimmerClick = false,
  UNSAFE_ignoreBackEvent = false,
  a11yIncludeHeaderInScroll = true,
  disableChildrenDragging = false,
  portalContainer,
  ...rest
}) {
  const [expanded, setExpanded] = useState(false);

  // ESC 키 닫기 — UNSAFE_ignoreBackEvent 로 억제 가능 (뒤로가기와 동일 취급).
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => {
      if (e.key === "Escape" && !UNSAFE_ignoreBackEvent) onClose?.();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose, UNSAFE_ignoreBackEvent]);

  // open 이 true → false 바뀔 때 onExited, 반대로 → true 일 때 onEntered.
  // (transition end 가 아닌 mount 타이밍 기준 — TDS 스펙이 보장하는 최소치)
  const prevOpen = React.useRef(open);
  useEffect(() => {
    if (prevOpen.current !== open) {
      if (open) onEntered?.();
      else onExited?.();
      prevOpen.current = open;
    }
  }, [open, onEntered, onExited]);

  // open 이 false 로 바뀌면 expanded 상태도 초기화
  useEffect(() => {
    if (!open && expanded) {
      setExpanded(false);
    }
  }, [open, expanded]);

  const handleDimmerClick = (e) => {
    onDimmerClick?.(e);
    if (!UNSAFE_ignoreDimmerClick) onClose?.();
  };

  const sheetMaxHeight =
    expanded && expandedMaxHeight
      ? `${expandedMaxHeight}px`
      : maxHeight
      ? `${maxHeight}px`
      : undefined;

  const rootCls = [
    "bottom-sheet-root",
    open ? "is-open" : "",
    hasTextField ? "bottom-sheet-root--textfield" : "",
  ]
    .filter(Boolean)
    .join(" ");

  const sheetCls = [
    "bottom-sheet",
    expanded ? "bottom-sheet--expanded" : "",
    cta ? "bottom-sheet--has-cta" : "",
    className,
  ]
    .filter(Boolean)
    .join(" ");

  const dimCls = ["bottom-sheet-root__dimmer", dimmerClassName]
    .filter(Boolean)
    .join(" ");

  // 본문 영역이 CTA 아래로 가려지지 않도록 CTA 가 있을 때 padding-bottom 을 더 준다.
  const bodyStyle = cta ? { paddingBottom: `${ctaContentGap}px` } : undefined;

  return (
    <div
      className={rootCls}
      aria-hidden={!open}
      {...rest}
    >
      {!disableDimmer && (
        <div className={dimCls} onClick={handleDimmerClick} />
      )}
      <div
        className={sheetCls}
        role="dialog"
        aria-modal={!UNSAFE_disableFocusLock}
        aria-labelledby={ariaLabelledBy}
        aria-describedby={ariaDescribedBy}
        style={{
          maxHeight: sheetMaxHeight,
          transform: open ? "translateY(0)" : "translateY(100%)",
        }}
      >
        <div
          className="bottom-sheet__handle"
          onTouchStart={onHandlerTouchStart}
          onTouchEnd={onHandlerTouchEnd}
          aria-hidden="true"
        >
          <span className="bottom-sheet__grabber" />
        </div>

        {(header || headerDescription) && (
          <div className="bottom-sheet__header-area">
            {header}
            {headerDescription}
          </div>
        )}

        <div className="bottom-sheet__body" style={bodyStyle}>
          {children}
        </div>

        {cta && <div className="bottom-sheet__cta-area">{cta}</div>}
      </div>
    </div>
  );
}

/* -------------------- BottomSheet.Header -------------------- */
/* 문자열을 받으면 h1 로 감싸고 t4 를 적용. ReactNode 면 그대로 전달. */
function BottomSheetHeader({ children, className = "", ...rest }) {
  const cls = ["bottom-sheet-header", className].filter(Boolean).join(" ");
  if (typeof children === "string" || typeof children === "number") {
    return <h1 className={cls} {...rest}>{children}</h1>;
  }
  return <div className={cls} {...rest}>{children}</div>;
}

/* -------------------- BottomSheet.HeaderDescription -------------------- */
function BottomSheetHeaderDescription({ children, className = "", ...rest }) {
  const cls = ["bottom-sheet-header-desc", className]
    .filter(Boolean)
    .join(" ");
  return <p className={cls} {...rest}>{children}</p>;
}

/* -------------------- BottomSheet.CTA -------------------- */
/* 하단 영역 전체 너비 주요 버튼. TDS Button (color="primary" variant="fill"
 * size="xlarge" display="block") 위에 .bottom-sheet-cta 클래스로 시트 컨텍스트
 * 스타일을 얹는다. loading 등 추가 prop 도 그대로 패스스루. */
function BottomSheetCTA({
  children,
  className = "",
  onClick,
  disabled,
  loading,
  type = "button",
  ...rest
}) {
  const cls = ["bottom-sheet-cta", className].filter(Boolean).join(" ");
  return (
    <Button
      color="primary"
      variant="fill"
      size="xlarge"
      display="block"
      type={type}
      className={cls}
      onClick={onClick}
      disabled={disabled}
      loading={loading}
      {...rest}
    >
      {children}
    </Button>
  );
}

/* -------------------- BottomSheet.DoubleCTA -------------------- */
/* leftButton / rightButton 두 자리. 일반적으로 좌=취소/약한 버튼, 우=주요 버튼. */
function BottomSheetDoubleCTA({
  leftButton,
  rightButton,
  className = "",
  ...rest
}) {
  const cls = ["bottom-sheet-double-cta", className]
    .filter(Boolean)
    .join(" ");
  return (
    <div className={cls} {...rest}>
      <div className="bottom-sheet-double-cta__left">{leftButton}</div>
      <div className="bottom-sheet-double-cta__right">{rightButton}</div>
    </div>
  );
}

/* -------------------- BottomSheet.Select -------------------- */
/* options: [{ name, value, className?, disabled?, hideUnCheckedCheckBox? }]
 * onChange: (e) => void — e.target.value 로 선택값 전달 (radio input 이벤트 그대로) */
function BottomSheetSelect({
  options = [],
  onChange,
  value,
  className = "",
  animation = true,
  animationDelay = 0,
  ...rest
}) {
  const cls = ["bottom-sheet-select", className]
    .filter(Boolean)
    .join(" ");
  // 라디오 그룹 이름은 한 페이지에서 여러 Select 가 있어도 겹치지 않게 랜덤.
  const groupNameRef = React.useRef(
    "bs-select-" + Math.random().toString(36).slice(2, 8)
  );
  return (
    <ul className={cls} role="radiogroup" {...rest}>
      {options.map((opt, i) => {
        const checked = String(opt.value) === String(value);
        const disabled = !!opt.disabled;
        const hideUnchecked = !!opt.hideUnCheckedCheckBox && !checked;
        const itemCls = [
          "bottom-sheet-select__option",
          checked ? "is-checked" : "",
          disabled ? "is-disabled" : "",
          opt.className || "",
        ]
          .filter(Boolean)
          .join(" ");
        const itemStyle = animation
          ? { animationDelay: `${animationDelay + i * 40}ms` }
          : undefined;
        return (
          <li key={opt.value} className={itemCls} style={itemStyle}>
            <label className="bottom-sheet-select__label">
              <input
                type="radio"
                className="bottom-sheet-select__radio"
                name={groupNameRef.current}
                value={opt.value}
                checked={checked}
                disabled={disabled}
                onChange={onChange}
              />
              <span className="bottom-sheet-select__name">{opt.name}</span>
              {!hideUnchecked && (
                <span className="bottom-sheet-select__check" aria-hidden="true">
                  {checked && <IOSIcon name="check" size={20} weight={2.4} />}
                </span>
              )}
            </label>
          </li>
        );
      })}
    </ul>
  );
}

/* -------------------- 서브컴포넌트 부착 -------------------- */
BottomSheet.Header = BottomSheetHeader;
BottomSheet.HeaderDescription = BottomSheetHeaderDescription;
BottomSheet.CTA = BottomSheetCTA;
BottomSheet.DoubleCTA = BottomSheetDoubleCTA;
BottomSheet.Select = BottomSheetSelect;

window.BottomSheet = BottomSheet;

Object.assign(window, { BottomSheet });
