import { Children, PropsWithChildren } from "react"

export interface CondProps {
  condition?: boolean
}

/**
 * Wrapper component that attaches a condition to a child component so that ChooseOne can
 * determine which child to render. The last Cond in a ChooseOne is the fallback case and
 * should not have a condition.
 * @param condition boolean expression indicating whether the child should be rendered, or undefined
 * @returns child. Note that Cond alone does not enforce the condition; it should be used inside ChooseOne.
 */
export const Cond = ({
  children,
}: PropsWithChildren<CondProps>): JSX.Element => {
  return <>{children}</>
}

/**
 * Wrapper component for rendering exactly one of its children. Wrap each child in Cond to associate it
 * with a condition under which it should be rendered. If no conditions are met, the final child
 * will be rendered.
 * @returns one of its children, or null if there are no children
 * @throws an error if its last child has a condition prop, or any non-final children do not have a condition prop
 */
export const ChooseOne = ({
  children,
}: PropsWithChildren): JSX.Element | null => {
  const childArray = Children.toArray(children) as JSX.Element[]
  if (childArray.length === 0) {
    return null
  }
  const conditionedOptions = childArray.slice(0, childArray.length - 1)
  const defaultCase = childArray[childArray.length - 1]
  if (defaultCase.props.condition !== undefined) {
    throw new Error(
      "The last Cond in a ChooseOne was given a condition prop, but it is the default case.",
    )
  }
  if (conditionedOptions.some((cond) => cond.props.condition === undefined)) {
    throw new Error(
      "A non-final Cond in a ChooseOne does not have a condition prop or the prop is undefined.",
    )
  }
  const chosen = conditionedOptions.find((child) => child.props.condition)
  return chosen ?? defaultCase
}
