"use client";

import {
  Children,
  Fragment,
  cloneElement,
  isValidElement,
  type ReactElement,
  type ReactNode,
  type Ref,
} from "react";
import { cx } from "../../utils";
import type { FoxCheckboxSize } from "../fox-checkbox";

export type FoxCheckboxGroupOrientation = "horizontal" | "vertical";

export interface FoxCheckboxGroupProps {
  orientation?: FoxCheckboxGroupOrientation;
  /**
   * 자식 체크박스의 `name`을 전부 이 값으로 덮어쓴다. 라디오와 달리 `name`이 같아도 서로를
   * 끄지 않는다 — 폼에서 같은 이름의 값 여러 개로 함께 전송될 뿐이다.
   */
  name?: string;
  /** 자식 체크박스의 `size`를 전부 이 값으로 덮어쓴다. 넘기지 않으면 자식이 각자 정한다. */
  size?: FoxCheckboxSize;
  /** 그룹을 읽어 줄 이름. 화면에 보이는 제목이 이미 있으면 `labelledBy`를 쓴다. */
  label?: string;
  /** 그룹 이름 역할을 하는 요소의 id. `label`보다 우선한다 — 화면의 글자와 어긋나지 않는다. */
  labelledBy?: string;
  /** `FoxCheckbox`들. */
  children?: ReactNode;
  /** 배치 조정용. */
  className?: string;
  ref?: Ref<HTMLDivElement>;
}

const ORIENTATION_CLASS: Record<FoxCheckboxGroupOrientation, string> = {
  horizontal: "fox-checkbox-group--horizontal",
  vertical: "fox-checkbox-group--vertical",
};

type Overrides = { name?: string; size?: FoxCheckboxSize };

/**
 * 자식 체크박스에 그룹 값을 강제로 덮어쓴다.
 *
 * - 호스트 요소(`div` 등)는 건드리지 않는다 — DOM에 없는 속성이 넘어가면 React가 경고한다.
 * - `Children.map`은 프래그먼트 안으로 들어가지 않아 직접 내려간다. 그러지 않으면
 *   `<>…</>`로 감싼 체크박스들이 덮어쓰기를 피해 가고 Fragment에 `name`이 붙어 경고가 난다.
 */
function overrideChildren(children: ReactNode, overrides: Overrides): ReactNode {
  return Children.map(children, (child) => {
    if (!isValidElement(child)) {
      return child;
    }

    if (child.type === Fragment) {
      const fragment = child as ReactElement<{ children?: ReactNode }>;
      return overrideChildren(fragment.props.children, overrides);
    }

    if (typeof child.type === "string") {
      return child;
    }

    return cloneElement(child as ReactElement<Record<string, unknown>>, overrides);
  });
}

/**
 * @fox 체크박스 묶음. 배치와 `name`·`size` 통일만 책임지고 상태를 갖지 않는다.
 *
 * `role="radiogroup"`이 아니라 `role="group"`이다 — 체크박스는 서로를 끄지 않아 "하나만
 * 고르는 묶음"이 아니고, 화살표 이동으로 옮겨 다니지도 않는다. 각 칸이 독립적으로 Tab을 받는다.
 *
 * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
 * (또는 개별 파티셜)로 한 번 불러와야 한다.
 */
export function FoxCheckboxGroup({
  orientation = "vertical",
  name,
  size,
  label,
  labelledBy,
  children,
  className,
  ref,
}: FoxCheckboxGroupProps) {
  // 값을 준 것만 넣는다 — `undefined`를 그대로 넘기면 자식이 스스로 정한 값을 지운다.
  const overrides: Overrides = {};
  if (name !== undefined) {
    overrides.name = name;
  }
  if (size !== undefined) {
    overrides.size = size;
  }

  return (
    <div
      ref={ref}
      role="group"
      aria-label={labelledBy ? undefined : label}
      aria-labelledby={labelledBy}
      className={cx("fox-checkbox-group", ORIENTATION_CLASS[orientation], className)}
    >
      {overrideChildren(children, overrides)}
    </div>
  );
}
