"use client";

import {
  Children,
  Fragment,
  cloneElement,
  createRef,
  isValidElement,
  type KeyboardEvent,
  type ReactElement,
  type ReactNode,
  type RefObject,
} from "react";
import { cx } from "../../utils";
import type { FoxTabItemProps } from "../fox-tab-item";

export type FoxTabType = "line" | "fill";
export type FoxTabSize = "lg" | "md";

export interface FoxTabProps {
  /** `FoxTabItem`들. */
  children?: ReactNode;
  /** `line`은 밑줄로, `fill`은 색을 채워 고른 탭을 표시한다. */
  type?: FoxTabType;
  /** `lg`는 폭을 고르게 나눠 가지고, `md`는 글자만큼만 차지하고 나란히 선다. */
  size?: FoxTabSize;
  /** 고른 탭의 `value`. */
  value?: string;
  onValueChange?: (value: string) => void;
  /** 목록을 읽어 줄 이름. */
  label?: string;
  /** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
  hidden?: boolean;
  /** 배치 조정용. */
  className?: string;
}

type ItemElement = ReactElement<FoxTabItemProps>;

/** 프래그먼트 안까지 훑어 탭을 모은다. */
function collect(children: ReactNode): ItemElement[] {
  const items: ItemElement[] = [];
  Children.forEach(children, (child) => {
    if (!isValidElement(child)) {
      return;
    }
    if (child.type === Fragment) {
      const fragment = child as ReactElement<{ children?: ReactNode }>;
      items.push(...collect(fragment.props.children));
      return;
    }
    items.push(child as ItemElement);
  });
  return items;
}

/**
 * @fox 탭 묶음. 어느 탭이 골라졌는지는 이 컴포넌트가 소유한다.
 *
 * 항목이 `href`를 하나라도 가지면 주소가 바뀌는 탭으로 보고 탭 역할(`role="tablist"`)을
 * 붙이지 않는다 — 같은 화면 안의 전환이 아니라 이동이기 때문이다. 그 밖에는 ARIA 탭
 * 규약대로 좌우 화살표·Home·End로 옮길 수 있고 탭키는 고른 탭에만 닿는다.
 *
 * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
 * (또는 개별 파티셜)로 한 번 불러와야 한다.
 */
export function FoxTab({
  children,
  type = "line",
  size = "lg",
  value,
  onValueChange,
  label = "탭",
  hidden = false,
  className,
}: FoxTabProps) {
  if (hidden) {
    return null;
  }

  const items = collect(children);
  const tabRole = !items.some((item) => item.props.href);
  const refs = items.map(() => createRef<HTMLElement>());

  // 잠긴 탭은 건너뛴다 — 옮겨 가도 고를 수 없어 막다른 길이 된다.
  const move = (from: number, step: number) => {
    const count = items.length;
    for (let hop = 1; hop <= count; hop += 1) {
      const next = (from + step * hop + count * count) % count;
      if (!items[next].props.disabled) {
        refs[next].current?.focus();
        return;
      }
    }
  };

  const navigate = (index: number) => (event: KeyboardEvent<HTMLElement>) => {
    if (!tabRole) {
      return;
    }
    if (event.key === "ArrowRight") {
      event.preventDefault();
      move(index, 1);
    } else if (event.key === "ArrowLeft") {
      event.preventDefault();
      move(index, -1);
    } else if (event.key === "Home") {
      event.preventDefault();
      move(-1, 1);
    } else if (event.key === "End") {
      event.preventDefault();
      move(0, -1);
    }
  };

  return (
    <div className={cx("fox-tab", `fox-tab--${type}`, `fox-tab--${size}`, className)}>
      <div
        className="fox-tab__list"
        role={tabRole ? "tablist" : undefined}
        aria-label={tabRole ? label : undefined}
      >
        {items.map((item, index) =>
          cloneElement(item, {
            key: item.key ?? item.props.value,
            active: item.props.value === value,
            tabRole,
            onSelect: onValueChange,
            onNavigate: navigate(index),
            ref: refs[index] as RefObject<HTMLElement>,
          })
        )}
      </div>
    </div>
  );
}
