<template>
  <div :class="descriptionKls">
    <div
      v-if="title || extra || $slots.title || $slots.extra"
      :class="ns.e('header')"
    >
      <div :class="ns.e('title')">
        <slot name="title">{{ title }}</slot>
      </div>
      <div :class="ns.e('extra')">
        <slot name="extra">{{ extra }}</slot>
      </div>
    </div>

    <div :class="ns.e('body')">
      <table :class="[ns.e('table'), ns.is('bordered', border)]">
        <tbody>
          <template v-for="(row, index) in getRows()" :key="index">
            <el-descriptions-row :row="row" />
          </template>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, provide } from 'vue'
import { isValidComponentSize } from '@element-ultra/utils'
import { useSize, useNamespace } from '@element-ultra/hooks'
import DescriptionsRow from './descriptions-row.vue'
import { elDescriptionsKey } from './token'

import type { PropType } from 'vue'
import type { ComponentSize } from '@element-ultra/shared'

export default defineComponent({
  name: 'ElDescriptions',
  components: {
    [DescriptionsRow.name]: DescriptionsRow,
  },
  props: {
    border: {
      type: Boolean,
      default: false,
    },
    column: {
      type: Number,
      default: 3,
    },
    direction: {
      type: String as PropType<'horizontal' | 'vertical'>,
      default: 'horizontal',
    },
    size: {
      type: String as PropType<ComponentSize>,
      validator: isValidComponentSize,
    },
    title: {
      type: String,
      default: '',
    },
    extra: {
      type: String,
      default: '',
    },
  },
  setup(props, { slots }) {
    provide(elDescriptionsKey, props)

    const descriptionsSize = useSize({ props })
    const ns = useNamespace('descriptions')

    const descriptionKls = computed(() => [
      ns.b(),
      ns.is(ns.m(descriptionsSize.value), !!descriptionsSize.value),
    ])

    const flattedChildren = (children) => {
      const temp = Array.isArray(children) ? children : [children]
      const res = []
      temp.forEach((child) => {
        if (Array.isArray(child.children)) {
          res.push(...flattedChildren(child.children))
        } else {
          res.push(child)
        }
      })
      return res
    }

    const filledNode = (node, span, count, isLast = false) => {
      if (!node.props) {
        node.props = {}
      }
      if (span > count) {
        node.props.span = count
      }
      if (isLast) {
        // set the last span
        node.props.span = span
      }
      return node
    }

    const getRows = () => {
      const children = flattedChildren(slots.default?.()).filter(
        (node) => node?.type?.name === 'ElDescriptionsItem'
      )
      const rows = []
      let temp = []
      let count = props.column
      let totalSpan = 0 // all spans number of item

      children.forEach((node, index) => {
        const span = node.props?.span || 1

        if (index < children.length - 1) {
          totalSpan += span > count ? count : span
        }

        if (index === children.length - 1) {
          // calculate the last item span
          const lastSpan = props.column - (totalSpan % props.column)
          temp.push(filledNode(node, lastSpan, count, true))
          rows.push(temp)
          return
        }

        if (span < count) {
          count -= span
          temp.push(node)
        } else {
          temp.push(filledNode(node, span, count))
          rows.push(temp)
          count = props.column
          temp = []
        }
      })

      return rows
    }

    return {
      descriptionKls,
      getRows,
      ns,
    }
  },
})
</script>
