<script lang="ts" setup>
import type { VNode } from 'vue';

import { Comment, Fragment, computed, useSlots } from 'vue';

export interface ZqDescRowItem {
  label: string;
  span: number;
  vnode: VNode;
}

const props = withDefaults(
  defineProps<{
    column?: number;
  }>(),
  {
    column: 2,
  },
);

const slots = useSlots();

function flattenVNodes(vnodes: VNode[]): VNode[] {
  const result: VNode[] = [];
  for (const vnode of vnodes) {
    if (!vnode || vnode.type === Comment) {
      continue;
    }
    if (vnode.type === Fragment && Array.isArray(vnode.children)) {
      result.push(...flattenVNodes(vnode.children as VNode[]));
      continue;
    }
    result.push(vnode);
  }
  return result;
}

function isDescItem(vnode: VNode) {
  const type = vnode.type as { name?: string; __name?: string } | string;
  if (typeof type === 'object') {
    return type.name === 'ZqDescItem' || type.__name === 'ZqDescItem';
  }
  return false;
}

const parsedRows = computed(() => {
  const vnodes = flattenVNodes(slots.default?.() ?? []);
  const items: ZqDescRowItem[] = vnodes
    .filter(isDescItem)
    .map((vnode) => ({
      label: String(vnode.props?.label ?? ''),
      span: Math.min(Number(vnode.props?.span ?? 1), props.column),
      vnode,
    }));

  const rows: ZqDescRowItem[][] = [];
  let currentRow: ZqDescRowItem[] = [];
  let usedColumns = 0;

  for (const item of items) {
    if (usedColumns + item.span > props.column) {
      if (currentRow.length > 0) {
        rows.push(currentRow);
      }
      currentRow = [];
      usedColumns = 0;
    }

    currentRow.push(item);
    usedColumns += item.span;

    if (usedColumns >= props.column) {
      rows.push(currentRow);
      currentRow = [];
      usedColumns = 0;
    }
  }

  if (currentRow.length > 0) {
    rows.push(currentRow);
  }

  return rows;
});

function getContentColspan(span: number) {
  return span * 2 - 1;
}

function renderItemContent(vnode: VNode) {
  const render = vnode.children as { default?: () => VNode[] } | undefined;
  return render?.default?.() ?? [];
}
</script>

<template>
  <div class="zq-desc">
    <table class="zq-desc__table">
      <tbody>
        <tr v-for="(row, rowIndex) in parsedRows" :key="rowIndex">
          <template v-for="(item, itemIndex) in row" :key="itemIndex">
            <th class="zq-desc__label">{{ item.label }}</th>
            <td
              class="zq-desc__content"
              :colspan="getContentColspan(item.span)"
            >
              <component :is="() => renderItemContent(item.vnode)" />
            </td>
          </template>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<style scoped>
.zq-desc {
  overflow: hidden;
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 8px;
}

.zq-desc__table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.zq-desc__label {
  width: 120px;
  padding: 12px 16px;
  font-size: 14px;
  font-weight: normal;
  color: hsl(var(--muted-foreground));
  text-align: left;
  vertical-align: top;
  background-color: hsl(var(--background-deep));
  border-right: 1px solid var(--el-border-color-lighter);
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.zq-desc__content {
  padding: 12px 16px;
  font-size: 14px;
  color: var(--el-text-color-primary);
  word-break: break-word;
  vertical-align: top;
  background-color: var(--el-bg-color);
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.zq-desc__table tr:last-child .zq-desc__label,
.zq-desc__table tr:last-child .zq-desc__content {
  border-bottom: none;
}
</style>
