<script setup lang="ts">
import type { HTMLAttributes, VNode } from 'vue';
import { Comment, Fragment, Text, computed, useSlots } from 'vue';
import { cn } from '@/lib/utils';

const props = defineProps<{
  class?: HTMLAttributes['class'];
  errors?: Array<string | { message: string | undefined } | undefined>;
}>();

const content = computed(() => {
  if (!props.errors || props.errors.length === 0) return null;

  const uniqueErrors = [
    ...new Map(
      props.errors.filter(Boolean).map((error) => {
        const message = typeof error === 'string' ? error : error?.message;
        return [message, error];
      }),
    ).values(),
  ];

  if (uniqueErrors.length === 1 && uniqueErrors[0]) {
    return typeof uniqueErrors[0] === 'string'
      ? uniqueErrors[0]
      : uniqueErrors[0].message;
  }

  return uniqueErrors.map((error) =>
    typeof error === 'string' ? error : error?.message,
  );
});

// NOTE: Vue template ref-unwrapping doesn't always play well with `typeof` checks.
// Use a helper to ensure we always work with the plain value.
const getContent = () => content.value;

const slots = useSlots();
const defaultSlotContent = computed(() => slots.default?.({}) ?? []);

function hasMeaningfulNodes(nodes: VNode[]): boolean {
  return nodes.some((node) => {
    if (node.type === Comment) return false;
    if (node.type === Text)
      return String(node.children ?? '').trim().length > 0;
    if (node.type === Fragment && Array.isArray(node.children))
      return hasMeaningfulNodes(node.children as VNode[]);
    return true;
  });
}

const hasDefaultSlotContent = computed(() =>
  hasMeaningfulNodes(defaultSlotContent.value),
);
</script>

<template>
  <div
    v-if="hasDefaultSlotContent || getContent()"
    role="alert"
    data-slot="field-error"
    :class="cn('body-14-regular text-destructive', props.class)"
  >
    <slot v-if="hasDefaultSlotContent" />

    <template v-else-if="typeof getContent() === 'string'">
      {{ getContent() }}
    </template>

    <ul
      v-else-if="Array.isArray(getContent())"
      class="ml-4 flex list-disc flex-col gap-1"
    >
      <li v-for="(error, index) in getContent()" :key="index">
        {{ error }}
      </li>
    </ul>
  </div>
</template>
