/* ============================================================================
 * 서명전에 — TDS Tab 컴포넌트
 *  · 한 화면에서 여러 콘텐츠를 효율적으로 전환하는 가로 탭 UI 요소예요.
 *  · 2 컴포넌트:
 *      Tab        — 컨테이너 (role="tablist")
 *      Tab.Item   — 각 탭 (role="tab" + aria-selected, redBean dot 옵션)
 *
 *  · 시각:
 *      탭 전체가 한 줄로 늘어서고, 컨테이너 하단에 1px grey-200 가이드 라인.
 *      선택된 Item 의 텍스트는 grey-900 + bold, 그 아래로 2px blue-500 underline
 *      indicator 가 슬라이드해서 따라와요. 비선택 Item 은 grey-500 + medium.
 *
 *  · alignment:
 *      fluid=false (기본) — 모든 아이템이 동일한 너비 (flex: 1 1 0).
 *                          최대 4 개 권장.
 *      fluid=true        — 아이템 너비 = 글자 수, 컨테이너를 넘으면 가로 스크롤.
 *                          5 개 이상이거나 텍스트 길이가 들쑥날쑥할 때.
 *
 *  · size:
 *      large (기본) — 높이 48px, 텍스트 sub-ty-5 (16px) medium / 선택 bold
 *      small        — 높이 36px, 텍스트 sub-ty-7 (14px) medium / 선택 bold
 *
 *  · itemGap:
 *      number — 아이템 사이 간격 (px). 기본은 fluid 면 24, fixed 면 0.
 *      fixed 모드에서 0 이 아닌 itemGap 을 주면 각 탭의 너비가 줄어들면서
 *      사이가 벌어져요.
 *
 *  · onChange (필수):
 *      (index, key?) => void.
 *      key 는 React 가 children 에 부여한 key. 호출자가 setSelected(index)
 *      처럼 외부 state 를 갱신해야 시각이 바뀌어요 (controlled-only).
 *
 *  · 접근성:
 *      role="tablist" (컨테이너) + aria-orientation="horizontal" + aria-label
 *      role="tab" (Item) + aria-selected (자동, selected 값으로) + tabIndex
 *        — selected=true 면 0, 나머지는 -1 (roving tabindex 표준 패턴)
 *      aria-label 누락 시 콘솔 경고. 라벨 문구에 "탭"/"Tab" 단어는 빼고
 *      기능만 적어요 — 스크린 리더가 이미 "탭" 으로 읽어줌.
 *      redBean=true 면 시각 + title="(업데이트 있음)" 으로 비시각 사용자에게도 안내.
 *
 *  · 키보드 (W3C tablist 표준):
 *      ←/↑ 이전 (wrap) · →/↓ 다음 (wrap) · Home 첫 · End 끝.
 *      Space/Enter 는 자동 — 버튼 click 으로 처리.
 *      방향키로 이동하면 onChange 도 호출되어 패널이 함께 바뀌어요
 *      (자동 활성화 모드 — TDS 스펙은 수동 활성화 별도 명시 없음).
 * ========================================================================== */

const __TAB_SIZES = { small: 1, large: 1 };

function Tab({
  children,
  onChange,
  size = "large",
  fluid = false,
  itemGap,
  ariaLabel,
  className = "",
  style,
  id,
  "aria-label": ariaLabelExt,
  "aria-labelledby": ariaLabelledBy,
  ...rest
}) {
  // ----- props 검증 -----
  if (!__TAB_SIZES[size]) {
    if (window.console && console.warn) {
      console.warn(
        `[Tab] size 는 "small" | "large" 만 가능해요. 받은 값: ${size}. "large" 사용.`
      );
    }
    size = "large";
  }
  if (typeof onChange !== "function") {
    if (window.console && console.warn) {
      console.warn(
        "[Tab] onChange 는 필수예요. (index, key?) => void 형태로 외부 state 를 갱신해야 해요."
      );
    }
  }

  // 외부에서 ariaLabel / aria-label / aria-labelledby 어떤 형태로든 들어올 수 있도록 통합
  const finalAriaLabel = ariaLabel != null ? ariaLabel : ariaLabelExt;
  if (!finalAriaLabel && !ariaLabelledBy) {
    if (window.console && console.warn) {
      console.warn(
        "[Tab] ariaLabel 또는 aria-labelledby 를 권장해요. 탭이 무엇을 분류하는지 " +
        "스크린 리더가 안내할 라벨이 필요해요. (\"탭\" 단어는 빼고 기능만 적어요)"
      );
    }
  }

  // ----- 자식 정리 -----
  const childArray = React.Children.toArray(children).filter(function (c) {
    return React.isValidElement(c);
  });
  const total = childArray.length;
  if (total > 4 && !fluid) {
    if (window.console && console.warn) {
      console.warn(
        `[Tab] fluid=false 일 때는 최대 4 개까지 권장해요. 받은 자식: ${total} 개. ` +
        "더 많을 땐 fluid 를 켜면 가로 스크롤로 보여줘요."
      );
    }
  }

  // ----- 현재 선택 인덱스 (selected prop 으로부터 추론) -----
  let selectedIndex = -1;
  for (let i = 0; i < total; i += 1) {
    const ch = childArray[i];
    if (ch.props && ch.props.selected) {
      selectedIndex = i;
      break;
    }
  }

  // ----- 활성화 (클릭 / 키보드 이동) -----
  const handleItemActivate = function (idx, key) {
    if (typeof onChange === "function") {
      onChange(idx, key);
    }
  };

  // 자식의 React key 추출 — child.key 는 cloneElement 이후에도 유지되지만 자식
  // 자신은 자기 key 를 못 읽음. 부모에서 미리 캡처해 onChange 두 번째 인자로 흘려요.
  const keyOf = function (child) {
    if (child && child.key !== null && child.key !== undefined) {
      // React 는 key 를 항상 string 으로 직렬화하지만, 숫자 key 가 들어왔을
      // 때를 위해 숫자형이 가능하면 number 로 복원해서 시그니처에 맞춰요.
      const k = child.key;
      if (typeof k === "string" && /^[-]?\d+$/.test(k)) {
        const n = Number(k);
        if (isFinite(n)) return n;
      }
      return k;
    }
    return undefined;
  };

  // ----- gap 결정 -----
  let resolvedGap = itemGap;
  if (typeof resolvedGap !== "number" || !isFinite(resolvedGap)) {
    resolvedGap = fluid ? 24 : 0;
  }

  // ----- 자식에 internal prop 주입 -----
  const enhanced = childArray.map(function (child, idx) {
    const isSelected = idx === selectedIndex;
    const childKey = keyOf(child);
    // 자식이 클릭/엔터/스페이스로 활성화될 때 key 를 함께 보내도록 바인딩
    const onActivateBound = function () {
      handleItemActivate(idx, childKey);
    };
    const onKeyDownBound = function (e) {
      // 키보드 방향키 이동도 같은 경로로. handleItemKeyDown 안에서
      // 다음 child 의 key 를 다시 풀어 onChange 에 전달.
      const k = e.key;
      let nextIdx = -1;
      if (k === "ArrowLeft" || k === "ArrowUp") {
        nextIdx = (idx - 1 + total) % total;
      } else if (k === "ArrowRight" || k === "ArrowDown") {
        nextIdx = (idx + 1) % total;
      } else if (k === "Home") {
        nextIdx = 0;
      } else if (k === "End") {
        nextIdx = total - 1;
      } else {
        return;
      }
      e.preventDefault();
      if (nextIdx === idx) return;
      const nextKey = keyOf(childArray[nextIdx]);
      handleItemActivate(nextIdx, nextKey);
    };
    return React.cloneElement(child, {
      __idx: idx,
      __size: size,
      __fluid: fluid,
      __isSelected: isSelected,
      __onActivate: onActivateBound,
      __onKeyDown: onKeyDownBound,
    });
  });

  // ----- 클래스 -----
  const wrapCls = [
    "tds-tab",
    `tds-tab--${size}`,
    fluid ? "tds-tab--fluid" : "tds-tab--fixed",
    className,
  ]
    .filter(Boolean)
    .join(" ");

  const wrapStyle = { ...(style || {}) };
  // gap 은 컨테이너 flex gap 으로 적용 (fluid/fixed 양쪽에서 동작)
  wrapStyle.gap = `${resolvedGap}px`;

  return (
    <div
      id={id}
      className={wrapCls}
      style={wrapStyle}
      role="tablist"
      aria-orientation="horizontal"
      aria-label={finalAriaLabel}
      aria-labelledby={ariaLabelledBy}
      {...rest}
    >
      {enhanced}
    </div>
  );
}

// =============================================================================
// Tab.Item
//   · role="tab" + aria-selected. 시각: 텍스트 + ::after underline indicator.
//   · redBean=true 면 우측 상단 빨간 6px 동그라미 + title="(업데이트 있음)".
//   · selected 가 외부 prop. 변경은 부모 Tab 의 onChange 가 받아 외부 state 갱신
//     → re-render 로 selected 가 바뀌는 controlled 패턴.
// =============================================================================
function TabItem({
  children,
  selected = false,
  redBean = false,
  className = "",
  style,
  id,
  onClick,
  // ----- 부모 Tab 가 주입하는 internal props -----
  __idx,
  __size,
  __fluid,
  __isSelected,
  __onActivate,
  __onKeyDown,
  "aria-label": ariaLabel,
  ...rest
}) {
  // 부모가 주입한 isSelected 가 진실 (children 의 selected 와 부모 인덱스 일치 여부)
  const isSelected = typeof __isSelected === "boolean" ? __isSelected : !!selected;

  const handleClick = function (e) {
    if (typeof onClick === "function") onClick(e);
    if (typeof __onActivate === "function") {
      // 부모가 idx + key 를 미리 바인딩해서 보냄 (Tab 의 enhanced.map 참고)
      __onActivate();
    }
  };

  const handleKeyDown = function (e) {
    if (typeof __onKeyDown === "function") {
      __onKeyDown(e);
    }
  };

  const cls = [
    "tds-tab__item",
    `tds-tab__item--${__size || "large"}`,
    __fluid ? "tds-tab__item--fluid" : "tds-tab__item--fixed",
    isSelected ? "is-selected" : "",
    redBean ? "has-red-bean" : "",
    className,
  ]
    .filter(Boolean)
    .join(" ");

  return (
    <button
      type="button"
      id={id}
      className={cls}
      style={style}
      role="tab"
      aria-selected={isSelected}
      tabIndex={isSelected ? 0 : -1}
      aria-label={ariaLabel}
      onClick={handleClick}
      onKeyDown={handleKeyDown}
      title={redBean ? "(업데이트 있음)" : undefined}
      {...rest}
    >
      <span className="tds-tab__item-label">{children}</span>
      {redBean ? (
        <span className="tds-tab__red-bean" aria-hidden="true" />
      ) : null}
    </button>
  );
}

Tab.Item = TabItem;

window.Tab = Tab;
window.TabItem = TabItem;
Object.assign(window, { Tab, TabItem });
