"use client";

import {
  Children,
  Fragment,
  cloneElement,
  isValidElement,
  type ReactElement,
  type ReactNode,
} from "react";
import { cx } from "../../utils";
import type { FoxBulletLevel, FoxBulletListItemProps } from "../fox-bullet-list-item";

export interface FoxBulletListProps {
  /** `FoxBulletListItem`들. 단계는 평평하게 늘어놓고 각 항목의 `level`로 적는다. */
  children: ReactNode;
  /** 참이면 번호 목록(`1.` · `a.` · `①`)이 되고, 아니면 기호 목록이다. */
  ordered?: boolean;
  /** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
  hidden?: boolean;
  /** 배치 조정용. */
  className?: string;
}

type ItemElement = ReactElement<FoxBulletListItemProps>;
type Entry = { level: FoxBulletLevel; element: ItemElement };

/** Fragment로 감싸 넘겨도 안쪽 항목까지 찾아낸다. */
function collect(children: ReactNode): Entry[] {
  const entries: Entry[] = [];
  Children.forEach(children, (child) => {
    if (!isValidElement(child)) {
      return;
    }
    if (child.type === Fragment) {
      const fragment = child as ReactElement<{ children?: ReactNode }>;
      entries.push(...collect(fragment.props.children));
      return;
    }
    const element = child as ItemElement;
    entries.push({ level: element.props.level ?? 1, element });
  });
  return entries;
}

/**
 * 평평한 단계 목록을 진짜 중첩으로 접는다.
 *
 * 더 깊은 항목들은 바로 앞 항목 **안쪽** 목록이 된다 — `<ul>`의 자식은 `<li>`뿐이라
 * 중첩 목록을 형제로 둘 수 없다. 앞에 걸어 둘 항목이 없으면(첫 줄부터 2단계로 시작하는
 * 경우) 빈 `<li>`에 매달아 유효한 목록을 지킨다.
 */
function build(entries: Entry[], depth: FoxBulletLevel, ordered: boolean): ReactNode[] {
  const nodes: ReactNode[] = [];
  let ordinal = 0;
  let index = 0;

  while (index < entries.length) {
    let next = index;

    if (entries[index].level > depth) {
      while (next < entries.length && entries[next].level > depth) {
        next += 1;
      }
      nodes.push(
        <li className="fox-bullet-list__hanger" key={`hanger-${index}`}>
          {sublist(entries.slice(index, next), depth, ordered)}
        </li>
      );
      index = next;
      continue;
    }

    next = index + 1;
    while (next < entries.length && entries[next].level > depth) {
      next += 1;
    }
    const deeper = entries.slice(index + 1, next);
    ordinal += 1;

    nodes.push(
      cloneElement(entries[index].element, {
        key: `item-${index}`,
        level: depth,
        ordered,
        ordinal,
        nested: deeper.length > 0 ? sublist(deeper, depth, ordered) : undefined,
      })
    );
    index = next;
  }

  return nodes;
}

function sublist(entries: Entry[], depth: FoxBulletLevel, ordered: boolean): ReactNode {
  const Tag = ordered ? "ol" : "ul";
  const deeper = Math.min(depth + 1, 3) as FoxBulletLevel;
  return (
    <Tag className="fox-bullet-list fox-bullet-list--nested">{build(entries, deeper, ordered)}</Tag>
  );
}

/**
 * @fox 글머리 목록. 항목의 `level`만 보고 중첩과 번호를 스스로 만든다.
 *
 * 호출부는 단계를 평평하게 늘어놓기만 하면 된다 — 어느 항목 안에 무엇을 넣을지 따질
 * 필요가 없다. 나가는 마크업은 `<ul>`/`<ol>` 안에 `<ul>`/`<ol>`이 든 진짜 중첩이라
 * 스크린리더가 몇 단계인지 알린다.
 *
 * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
 * (또는 개별 파티셜)로 한 번 불러와야 한다.
 */
export function FoxBulletList({
  children,
  ordered = false,
  hidden = false,
  className,
}: FoxBulletListProps) {
  if (hidden) {
    return null;
  }

  const entries = collect(children);
  if (entries.length === 0) {
    return null;
  }

  const Tag = ordered ? "ol" : "ul";

  return (
    <Tag className={cx("fox-bullet-list", className)}>{build(entries, 1, ordered)}</Tag>
  );
}
