import {createElement, ReactElement, ReactNode} from 'react'

// new function for type reasons
export function classToTw(element: ReactElement) {
  return replaceTw(element) as ReactElement
}

/** Traverse react element's tree, replacing className prop with tw for satori engine */
function replaceTw(element: ReactNode): ReactNode {
  // base case
  if (!element || typeof element !== 'object') {
    if (typeof element === 'string') return element
    return element
  }

  // fragment
  if (!('type' in element)) {
    // Convert to array safely
    const children = Array.isArray(element) ? element : [element]
    return <>{children.map(replaceTw)}</>
  }

  // component
  if (typeof element.type === 'function') {
    if (element.type.prototype?.isReactComponent) {
      throw Error(
        'React class component not supported in classname to tw middleware because Sinclair is lazy.',
      )
    }

    // functional component
    const component = element.type as (props: any) => ReactElement

    const newType = (props: any) => replaceTw(component(props)) as ReactElement
    return createElement(newType, element.props)
  }

  // pure element

  // Replace `className` with `tw` for this element
  const props = element.props as {className?: string; children?: ReactNode; tw?: string}
  const newProps = {...props}
  if (props.className) {
    newProps.tw = props.className
    delete newProps.className
  }

  // Recursively replace children, whether we have many, one, or no children
  const {children} = props
  const newChildren = children
    ? Array.isArray(children)
      ? children.map(replaceTw)
      : [replaceTw(children)]
    : []

  return createElement(element.type, newProps, newChildren)
}
