/* ============================================================================
 * 서명전에 — TDS Slider 컴포넌트
 *  · 막대를 좌우로 움직여서 원하는 숫자를 선택할 수 있는 컨트롤이에요.
 *  · 2 서브:
 *      Slider          — 본체 (track + fill + handle + label + tooltip)
 *      Slider.Tooltip  — 핸들 위에 표시되는 말풍선 (요소 자체로 전달)
 *
 *  · controlled / uncontrolled 모두 지원:
 *      controlled   — value + onValueChange(v)
 *      uncontrolled — defaultValue + 내부 useState
 *
 *  · props:
 *      minValue (number, 기본 0)
 *      maxValue (number, 기본 100)
 *      step     (number, 기본 1)            ← 스펙에는 없지만 키보드/클릭 정합 위해 노출
 *      color    (CSS color, 기본 var(--tds-blue-500))
 *      label    ({ min: string, max: string, mid?: string })
 *      tooltip  (React.ReactElement, 보통 <Slider.Tooltip message="..." />)
 *      disabled (boolean)
 *
 *  · 상호작용:
 *      · 트랙 / 핸들 위에서 pointerdown → setPointerCapture → pointermove 추적
 *      · 트랙 클릭 시 클릭 위치로 즉시 점프 (드래그 전이도 됨)
 *      · 키보드:
 *          ←  ↓  : -step      →  ↑  : +step
 *          PageDown: -step×10  PageUp: +step×10
 *          Home: minValue      End: maxValue
 *      · disabled 시 모든 인터랙션 차단 + 스타일 흐리게
 *
 *  · 접근성:
 *      role="slider", aria-valuemin / aria-valuemax / aria-valuenow,
 *      aria-orientation="horizontal", tabIndex=0,
 *      touch-action: none (모바일 가로 스와이프 방지),
 *      라벨이 있으면 aria-valuetext = `${valuenow}` (스펙 단순화),
 *      외부 aria-label / aria-labelledby 그대로 전달.
 *
 *  · color 변수 주입:
 *      --tds-slider-color (인라인 style) → CSS 가 fill / handle ring / tooltip 에 사용.
 * ========================================================================== */

function __sliderClampRound(raw, min, max, step) {
  let v = Math.max(min, Math.min(max, raw));
  if (step > 0) {
    // step 기준으로 스냅 — min 부터 떨어진 거리를 step 으로 round
    const snapped = Math.round((v - min) / step) * step + min;
    v = Math.max(min, Math.min(max, snapped));
  }
  return v;
}

function Slider({
  value,
  defaultValue,
  onValueChange,
  minValue = 0,
  maxValue = 100,
  step = 1,
  color,
  label,
  tooltip,
  disabled = false,
  className = "",
  style,
  id,
  "aria-label": ariaLabel,
  "aria-labelledby": ariaLabelledBy,
  ...rest
}) {
  // ----- props 검증 -----
  if (typeof minValue !== "number" || typeof maxValue !== "number" || minValue >= maxValue) {
    if (window.console && console.warn) {
      console.warn(
        `[Slider] minValue (${minValue}) 는 maxValue (${maxValue}) 보다 작아야 해요.`
      );
    }
  }
  if (typeof step !== "number" || step <= 0) {
    if (window.console && console.warn) {
      console.warn(`[Slider] step 은 양수여야 해요. 받은 값: ${step}. 1 사용.`);
    }
    step = 1;
  }

  // ----- controlled vs uncontrolled -----
  const isControlled = value !== undefined;
  const [internal, setInternal] = React.useState(function () {
    const init = typeof defaultValue === "number" ? defaultValue : minValue;
    return __sliderClampRound(init, minValue, maxValue, step);
  });
  const currentValue = isControlled
    ? __sliderClampRound(value, minValue, maxValue, step)
    : internal;

  if (isControlled && typeof onValueChange !== "function") {
    if (window.console && console.warn) {
      console.warn(
        "[Slider] value 만 주고 onValueChange 는 안 줬어요. " +
        "controlled 모드에서는 onValueChange 로 외부 상태를 갱신해야 해요."
      );
    }
  }

  const commitValue = function (next) {
    const v = __sliderClampRound(next, minValue, maxValue, step);
    if (v === currentValue) return;
    if (!isControlled) setInternal(v);
    if (typeof onValueChange === "function") onValueChange(v);
  };

  // ----- 진행률 (0 ~ 1) -----
  const denom = maxValue - minValue;
  const ratio = denom > 0 ? (currentValue - minValue) / denom : 0;
  const percent = Math.max(0, Math.min(1, ratio)) * 100;

  // ----- 트랙 → 좌표 헬퍼 -----
  const trackRef = React.useRef(null);
  const draggingRef = React.useRef(false);
  const pointerIdRef = React.useRef(null);

  const valueFromClientX = function (clientX) {
    const el = trackRef.current;
    if (!el) return currentValue;
    const rect = el.getBoundingClientRect();
    if (rect.width <= 0) return currentValue;
    const r = (clientX - rect.left) / rect.width;
    const clamped = Math.max(0, Math.min(1, r));
    return minValue + clamped * (maxValue - minValue);
  };

  // ----- 포인터 이벤트 -----
  const handlePointerDown = function (e) {
    if (disabled) return;
    // 좌클릭 / 터치 / 펜만 처리
    if (e.button !== undefined && e.button !== 0) return;

    const el = trackRef.current;
    if (!el) return;
    try {
      el.setPointerCapture(e.pointerId);
    } catch (_) { /* 일부 브라우저에서 실패 가능 — 무시 */ }
    draggingRef.current = true;
    pointerIdRef.current = e.pointerId;

    // 즉시 점프
    commitValue(valueFromClientX(e.clientX));
    e.preventDefault();
  };

  const handlePointerMove = function (e) {
    if (!draggingRef.current) return;
    if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) return;
    commitValue(valueFromClientX(e.clientX));
  };

  const handlePointerUp = function (e) {
    if (!draggingRef.current) return;
    if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) return;
    draggingRef.current = false;
    pointerIdRef.current = null;
    const el = trackRef.current;
    if (el) {
      try { el.releasePointerCapture(e.pointerId); } catch (_) {}
    }
  };

  // ----- 키보드 -----
  const handleKeyDown = function (e) {
    if (disabled) return;
    const k = e.key;
    let next = currentValue;
    if (k === "ArrowLeft" || k === "ArrowDown") {
      next = currentValue - step;
    } else if (k === "ArrowRight" || k === "ArrowUp") {
      next = currentValue + step;
    } else if (k === "PageDown") {
      next = currentValue - step * 10;
    } else if (k === "PageUp") {
      next = currentValue + step * 10;
    } else if (k === "Home") {
      next = minValue;
    } else if (k === "End") {
      next = maxValue;
    } else {
      return;
    }
    e.preventDefault();
    commitValue(next);
  };

  // ----- 색 주입 — CSS 변수 --tds-slider-color -----
  const wrapStyle = { ...(style || {}) };
  if (color) {
    wrapStyle["--tds-slider-color"] = color;
  }

  // ----- 라벨 평가 -----
  const hasLabel = label && (label.min || label.max || label.mid);

  // ----- 툴팁 — message 만 받아 우리가 위치를 잡음 -----
  const renderTooltip = function () {
    if (!tooltip) return null;
    if (!React.isValidElement(tooltip)) {
      if (window.console && console.warn) {
        console.warn("[Slider] tooltip 은 React 요소여야 해요. (예: <Slider.Tooltip message=\"...\" />)");
      }
      return null;
    }
    // 자식의 className/style 에 위치를 주입 — 핸들 가운데 위에 정렬
    const childClass = tooltip.props && tooltip.props.className ? tooltip.props.className : "";
    const childStyle = tooltip.props && tooltip.props.style ? tooltip.props.style : {};
    const merged = {
      ...childStyle,
      left: `${percent}%`,
    };
    return React.cloneElement(tooltip, {
      className: ["tds-slider__tooltip-slot", childClass].filter(Boolean).join(" "),
      style: merged,
    });
  };

  // ----- 클래스 조립 -----
  const wrapCls = [
    "tds-slider",
    disabled ? "tds-slider--disabled" : "",
    hasLabel ? "tds-slider--has-label" : "",
    tooltip ? "tds-slider--has-tooltip" : "",
    className,
  ]
    .filter(Boolean)
    .join(" ");

  return (
    <div id={id} className={wrapCls} style={wrapStyle} {...rest}>
      {tooltip ? (
        <div className="tds-slider__tooltip-layer" aria-hidden="true">
          {renderTooltip()}
        </div>
      ) : null}

      <div
        ref={trackRef}
        className="tds-slider__track"
        onPointerDown={handlePointerDown}
        onPointerMove={handlePointerMove}
        onPointerUp={handlePointerUp}
        onPointerCancel={handlePointerUp}
      >
        <div className="tds-slider__fill" style={{ width: `${percent}%` }} />
        <div
          className="tds-slider__handle"
          style={{ left: `${percent}%` }}
          role="slider"
          tabIndex={disabled ? -1 : 0}
          aria-valuemin={minValue}
          aria-valuemax={maxValue}
          aria-valuenow={currentValue}
          aria-orientation="horizontal"
          aria-disabled={disabled || undefined}
          aria-label={ariaLabel}
          aria-labelledby={ariaLabelledBy}
          onKeyDown={handleKeyDown}
        />
      </div>

      {hasLabel ? (
        <div className="tds-slider__labels">
          <span className="tds-slider__label tds-slider__label--min">
            {label.min || ""}
          </span>
          {label.mid ? (
            <span className="tds-slider__label tds-slider__label--mid">
              {label.mid}
            </span>
          ) : null}
          <span className="tds-slider__label tds-slider__label--max">
            {label.max || ""}
          </span>
        </div>
      ) : null}
    </div>
  );
}

// =============================================================================
// Slider.Tooltip
//   · 핸들 위에 떠 있는 어두운 말풍선 + 아래로 향한 화살표.
//   · message: string (필수)
//   · 위치는 부모 Slider 가 React.cloneElement 로 left% 를 주입함.
//   · Tooltip 컴포넌트 자체와 이름이 겹치지 않도록 SliderTooltip 으로 노출.
// =============================================================================
function SliderTooltip({
  message,
  className = "",
  style,
  id,
  ...rest
}) {
  if (typeof message !== "string" || message.length === 0) {
    if (window.console && console.warn) {
      console.warn("[Slider.Tooltip] message 는 비어있지 않은 string 이어야 해요.");
    }
  }
  const cls = ["tds-slider__tooltip", className].filter(Boolean).join(" ");
  return (
    <div id={id} className={cls} style={style} role="presentation" {...rest}>
      <span className="tds-slider__tooltip-text">{message}</span>
      <span className="tds-slider__tooltip-arrow" aria-hidden="true" />
    </div>
  );
}

Slider.Tooltip = SliderTooltip;

window.Slider = Slider;
window.SliderTooltip = SliderTooltip;
Object.assign(window, { Slider, SliderTooltip });
