<script lang="ts">
import {
  Comment,
  Fragment,
  Text,
  cloneVNode,
  defineComponent,
  h,
  withDirectives,
} from 'vue'

// eslint-disable-next-line vue/prefer-import-from-vue
import { isObject } from '@vue/shared'
import type { VNode } from 'vue'
import useClassname from '../../composables/use-classname'

export default defineComponent({
  name: 'UiRenderless',
  setup(_, { slots, attrs }) {
    return () => {
      const defaultSlot = slots.default?.(attrs)
      if (!defaultSlot)
        return null

      if (defaultSlot.length > 1)
        return null

      const firstLegitNode = findFirstLegitChild(defaultSlot)
      if (!firstLegitNode)
        return null

      return withDirectives(cloneVNode(firstLegitNode!, attrs, true), [])
    }
  },
})

function findFirstLegitChild(node: VNode[] | undefined): VNode | null {
  if (!node)
    return null
  const children = node as VNode[]
  for (const child of children) {
    /**
     * when user uses h(Fragment, [text]) to render plain string,
     * this switch case just cannot handle, when the value is primitives
     * we should just return the wrapped string
     */
    if (isObject(child)) {
      switch (child.type) {
        case Comment:
          continue
        case Text:
        case 'svg':
          return wrapTextContent(child)
        case Fragment:
          return findFirstLegitChild(child.children as VNode[])
        default:
          return child
      }
    }
    return wrapTextContent(child)
  }
  return null
}

function wrapTextContent(s: string | VNode) {
  const cn = useClassname('ui-renderless')
  return h('span', { class: cn.e('content') }, [s])
}
</script>
