<script setup lang="ts">
/**
 * Props-driven `card-list` composition — renders a list of nested
 * DisplayCards via `CardRenderer`. Each child card is a fully-resolved
 * DisplayCard (not an ID lookup).
 *
 * Self-contained — directly mountable in any Vue app:
 *
 *   <CardListCard
 *     title="Settings"
 *     :cards="[generalCard, advancedCard]"
 *     :item="dataModel"
 *   />
 *
 * Drag-and-drop reordering is a lightweight HTML5 implementation when
 * `draggable === true`; on drop the new card order is emitted as
 * `reorder` so a parent (e.g. the FormBuilder) can persist it.
 */
import { computed, ref } from "vue"
import type { DisplayCard } from "@/definitions/cards"
import CardRenderer from "@/engine/CardRenderer.vue"
import {
  Card,
  CardContent,
  CardHeader,
  CardTitle,
  CardDescription,
} from "@/components/ui/card"

export interface CardListCardProps {
  title?: string
  subtitle?: string
  showCard?: boolean
  draggable?: boolean
  dragTargetData?: string
  gutterY?: string
  gutterX?: string
  /** Resolved child cards (NOT IDs). */
  cards?: DisplayCard[]
  /** Page-level data model passed through to child renderers. */
  item?: Record<string, unknown>
  /** Identifier used as a fallback drag payload when `dragTargetData` is unset. */
  id?: string
}

const props = withDefaults(defineProps<CardListCardProps>(), {
  showCard: true,
  draggable: false,
  gutterY: "1rem",
  gutterX: "1rem",
  cards: () => [],
  item: () => ({}),
})

const emit = defineEmits<{
  (e: "reorder", value: DisplayCard[]): void
}>()

const internalOrder = ref<DisplayCard[] | null>(null)

const order = computed<DisplayCard[]>(
  () => internalOrder.value ?? props.cards,
)

const showChrome = computed<boolean>(() => props.showCard !== false)

const gridStyle = computed(() => ({
  rowGap: props.gutterY,
  columnGap: props.gutterX,
}))

const dragIndex = ref<number | null>(null)

function onDragStart(event: DragEvent, index: number): void {
  if (!props.draggable) {
    return
  }
  dragIndex.value = index
  if (event.dataTransfer) {
    event.dataTransfer.effectAllowed = "move"
    event.dataTransfer.setData(
      "text/plain",
      props.dragTargetData ?? props.id ?? "",
    )
  }
}

function onDragOver(event: DragEvent): void {
  if (!props.draggable) {
    return
  }
  event.preventDefault()
  if (event.dataTransfer) {
    event.dataTransfer.dropEffect = "move"
  }
}

function onDrop(event: DragEvent, targetIndex: number): void {
  if (!props.draggable || dragIndex.value === null) {
    return
  }
  event.preventDefault()
  const next = [...order.value]
  const [moved] = next.splice(dragIndex.value, 1)
  next.splice(targetIndex, 0, moved)
  internalOrder.value = next
  dragIndex.value = null
  emit("reorder", next)
}

function onDragEnd(): void {
  dragIndex.value = null
}
</script>

<template>
  <component
    :is="showChrome ? Card : 'section'"
    :class="[
      'gxp-card-list',
      showChrome ? '' : 'flex flex-col bg-transparent border-0 shadow-none',
    ]"
  >
    <CardHeader v-if="showChrome && (title || subtitle)">
      <CardTitle v-if="title">{{ title }}</CardTitle>
      <CardDescription v-if="subtitle">
        {{ subtitle }}
      </CardDescription>
    </CardHeader>
    <header
      v-else-if="title || subtitle"
      class="mb-3 flex flex-col gap-0.5"
    >
      <h3 v-if="title" class="text-base font-semibold">
        {{ title }}
      </h3>
      <p v-if="subtitle" class="text-sm text-muted-foreground">
        {{ subtitle }}
      </p>
    </header>

    <component
      :is="showChrome ? CardContent : 'div'"
      class="flex flex-col"
      :style="gridStyle"
    >
      <template v-if="order.length === 0">
        <p
          class="text-sm text-muted-foreground italic"
          data-testid="card-list-empty"
        >
          No cards to display.
        </p>
      </template>
      <template v-else>
        <div
          v-for="(child, index) in order"
          :key="child.id"
          :draggable="draggable === true"
          :data-card-list-item-id="child.id"
          :class="[
            'gxp-card-list__item',
            draggable ? 'cursor-move' : '',
            dragIndex === index ? 'opacity-50' : '',
          ]"
          @dragstart="onDragStart($event, index)"
          @dragover="onDragOver"
          @drop="onDrop($event, index)"
          @dragend="onDragEnd"
        >
          <CardRenderer :card="child" :item="item" />
        </div>
      </template>
    </component>
  </component>
</template>
